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 pg_noinline void prepare_tuplestore (WindowAggState *winstate)
 
static pg_noinline void calculate_frame_offsets (PlanState *pstate)
 
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  (Node *) winstate, NULL);
343  fcinfo->args[0].value = peraggstate->transValue;
344  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
345  winstate->curaggcontext = peraggstate->aggcontext;
346  newVal = FunctionCallInvoke(fcinfo);
347  winstate->curaggcontext = NULL;
348 
349  /*
350  * Moving-aggregate transition functions must not return null, see
351  * advance_windowaggregate_base().
352  */
353  if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
354  ereport(ERROR,
355  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
356  errmsg("moving-aggregate transition function must not return null")));
357 
358  /*
359  * We must track the number of rows included in transValue, since to
360  * remove the last input, advance_windowaggregate_base() mustn't call the
361  * inverse transition function, but simply reset transValue back to its
362  * initial value.
363  */
364  peraggstate->transValueCount++;
365 
366  /*
367  * If pass-by-ref datatype, must copy the new value into aggcontext and
368  * free the prior transValue. But if transfn returned a pointer to its
369  * first input, we don't need to do anything. Also, if transfn returned a
370  * pointer to a R/W expanded object that is already a child of the
371  * aggcontext, assume we can adopt that value without copying it. (See
372  * comments for ExecAggCopyTransValue, which this code duplicates.)
373  */
374  if (!peraggstate->transtypeByVal &&
375  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
376  {
377  if (!fcinfo->isnull)
378  {
379  MemoryContextSwitchTo(peraggstate->aggcontext);
381  false,
382  peraggstate->transtypeLen) &&
384  /* do nothing */ ;
385  else
386  newVal = datumCopy(newVal,
387  peraggstate->transtypeByVal,
388  peraggstate->transtypeLen);
389  }
390  if (!peraggstate->transValueIsNull)
391  {
393  false,
394  peraggstate->transtypeLen))
395  DeleteExpandedObject(peraggstate->transValue);
396  else
397  pfree(DatumGetPointer(peraggstate->transValue));
398  }
399  }
400 
401  MemoryContextSwitchTo(oldContext);
402  peraggstate->transValue = newVal;
403  peraggstate->transValueIsNull = fcinfo->isnull;
404 }
#define Assert(condition)
Definition: c.h:812
#define OidIsValid(objectId)
Definition: c.h:729
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:351
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:72
void pfree(void *pointer)
Definition: mcxt.c:1521
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:731
void * arg
#define FUNC_MAX_ARGS
#define lfirst(lc)
Definition: pg_list.h:172
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
MemoryContextSwitchTo(old_ctx)
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:266
bool fn_strict
Definition: fmgr.h:61
Definition: nodes.h:129
MemoryContext curaggcontext
Definition: execnodes.h:2651
ExprContext * tmpcontext
Definition: execnodes.h:2652
ExprState * aggfilter
Definition: execnodes.h:885
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  (Node *) winstate, NULL);
514  fcinfo->args[0].value = peraggstate->transValue;
515  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
516  winstate->curaggcontext = peraggstate->aggcontext;
517  newVal = FunctionCallInvoke(fcinfo);
518  winstate->curaggcontext = NULL;
519 
520  /*
521  * If the function returns NULL, report failure, forcing a restart.
522  */
523  if (fcinfo->isnull)
524  {
525  MemoryContextSwitchTo(oldContext);
526  return false;
527  }
528 
529  /* Update number of rows included in transValue */
530  peraggstate->transValueCount--;
531 
532  /*
533  * If pass-by-ref datatype, must copy the new value into aggcontext and
534  * free the prior transValue. But if invtransfn returned a pointer to its
535  * first input, we don't need to do anything. Also, if invtransfn
536  * returned a pointer to a R/W expanded object that is already a child of
537  * the aggcontext, assume we can adopt that value without copying it. (See
538  * comments for ExecAggCopyTransValue, which this code duplicates.)
539  *
540  * Note: the checks for null values here will never fire, but it seems
541  * best to have this stanza look just like advance_windowaggregate.
542  */
543  if (!peraggstate->transtypeByVal &&
544  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
545  {
546  if (!fcinfo->isnull)
547  {
548  MemoryContextSwitchTo(peraggstate->aggcontext);
550  false,
551  peraggstate->transtypeLen) &&
553  /* do nothing */ ;
554  else
555  newVal = datumCopy(newVal,
556  peraggstate->transtypeByVal,
557  peraggstate->transtypeLen);
558  }
559  if (!peraggstate->transValueIsNull)
560  {
562  false,
563  peraggstate->transtypeLen))
564  DeleteExpandedObject(peraggstate->transValue);
565  else
566  pfree(DatumGetPointer(peraggstate->transValue));
567  }
568  }
569 
570  MemoryContextSwitchTo(oldContext);
571  peraggstate->transValue = newVal;
572  peraggstate->transValueIsNull = fcinfo->isnull;
573 
574  return true;
575 }
#define elog(elevel,...)
Definition: elog.h:225
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
WindowStatePerFunc perfunc
Definition: execnodes.h:2599

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

3111 {
3112  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3113  ExprContext *econtext = winstate->tmpcontext;
3114 
3115  /* If no ORDER BY, all rows are peers with each other */
3116  if (node->ordNumCols == 0)
3117  return true;
3118 
3119  econtext->ecxt_outertuple = slot1;
3120  econtext->ecxt_innertuple = slot2;
3121  return ExecQualAndReset(winstate->ordEqfunction, econtext);
3122 }
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:458
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
Plan * plan
Definition: execnodes.h:1126
PlanState ps
Definition: execnodes.h:1573
ScanState ss
Definition: execnodes.h:2592
ExprState * ordEqfunction
Definition: execnodes.h:2602
int ordNumCols
Definition: plannodes.h:1059

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

1194 {
1195  PlanState *outerPlan = outerPlanState(winstate);
1196  int numfuncs = winstate->numfuncs;
1197 
1198  winstate->partition_spooled = false;
1199  winstate->framehead_valid = false;
1200  winstate->frametail_valid = false;
1201  winstate->grouptail_valid = false;
1202  winstate->spooled_rows = 0;
1203  winstate->currentpos = 0;
1204  winstate->frameheadpos = 0;
1205  winstate->frametailpos = 0;
1206  winstate->currentgroup = 0;
1207  winstate->frameheadgroup = 0;
1208  winstate->frametailgroup = 0;
1209  winstate->groupheadpos = 0;
1210  winstate->grouptailpos = -1; /* see update_grouptailpos */
1211  ExecClearTuple(winstate->agg_row_slot);
1212  if (winstate->framehead_slot)
1213  ExecClearTuple(winstate->framehead_slot);
1214  if (winstate->frametail_slot)
1215  ExecClearTuple(winstate->frametail_slot);
1216 
1217  /*
1218  * If this is the very first partition, we need to fetch the first input
1219  * row to store in first_part_slot.
1220  */
1221  if (TupIsNull(winstate->first_part_slot))
1222  {
1223  TupleTableSlot *outerslot = ExecProcNode(outerPlan);
1224 
1225  if (!TupIsNull(outerslot))
1226  ExecCopySlot(winstate->first_part_slot, outerslot);
1227  else
1228  {
1229  /* outer plan is empty, so we have nothing to do */
1230  winstate->partition_spooled = true;
1231  winstate->more_partitions = false;
1232  return;
1233  }
1234  }
1235 
1236  /* Create new tuplestore if not done already. */
1237  if (unlikely(winstate->buffer == NULL))
1238  prepare_tuplestore(winstate);
1239 
1240  winstate->next_partition = false;
1241 
1242  if (winstate->numaggs > 0)
1243  {
1244  WindowObject agg_winobj = winstate->agg_winobj;
1245 
1246  /* reset mark and see positions for aggregate functions */
1247  agg_winobj->markpos = -1;
1248  agg_winobj->seekpos = -1;
1249 
1250  /* Also reset the row counters for aggregates */
1251  winstate->aggregatedbase = 0;
1252  winstate->aggregatedupto = 0;
1253  }
1254 
1255  /* reset mark and seek positions for each real window function */
1256  for (int i = 0; i < numfuncs; i++)
1257  {
1258  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1259 
1260  if (!perfuncstate->plain_agg)
1261  {
1262  WindowObject winobj = perfuncstate->winobj;
1263 
1264  winobj->markpos = -1;
1265  winobj->seekpos = -1;
1266  }
1267  }
1268 
1269  /*
1270  * Store the first tuple into the tuplestore (it's always available now;
1271  * we either read it above, or saved it at the end of previous partition)
1272  */
1273  tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1274  winstate->spooled_rows++;
1275 }
#define unlikely(x)
Definition: c.h:330
#define outerPlanState(node)
Definition: execnodes.h:1222
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:272
static pg_noinline void prepare_tuplestore(WindowAggState *winstate)
#define outerPlan(node)
Definition: plannodes.h:183
int64 aggregatedbase
Definition: execnodes.h:2614
int64 frametailgroup
Definition: execnodes.h:2645
int64 frameheadgroup
Definition: execnodes.h:2644
TupleTableSlot * framehead_slot
Definition: execnodes.h:2669
bool next_partition
Definition: execnodes.h:2657
bool frametail_valid
Definition: execnodes.h:2662
bool partition_spooled
Definition: execnodes.h:2655
int64 spooled_rows
Definition: execnodes.h:2608
int64 frameheadpos
Definition: execnodes.h:2610
bool more_partitions
Definition: execnodes.h:2658
int64 grouptailpos
Definition: execnodes.h:2647
int64 currentgroup
Definition: execnodes.h:2643
TupleTableSlot * frametail_slot
Definition: execnodes.h:2670
Tuplestorestate * buffer
Definition: execnodes.h:2603
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2673
struct WindowObjectData * agg_winobj
Definition: execnodes.h:2613
bool framehead_valid
Definition: execnodes.h:2660
int64 groupheadpos
Definition: execnodes.h:2646
bool grouptail_valid
Definition: execnodes.h:2664
int64 currentpos
Definition: execnodes.h:2609
int64 frametailpos
Definition: execnodes.h:2611
TupleTableSlot * first_part_slot
Definition: execnodes.h:2667
int64 aggregatedupto
Definition: execnodes.h:2615
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509
#define TupIsNull(slot)
Definition: tuptable.h:306

References WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggregatedbase, WindowAggState::aggregatedupto, WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, ExecClearTuple(), ExecCopySlot(), ExecProcNode(), WindowAggState::first_part_slot, WindowAggState::framehead_slot, WindowAggState::framehead_valid, WindowAggState::frameheadgroup, WindowAggState::frameheadpos, WindowAggState::frametail_slot, WindowAggState::frametail_valid, WindowAggState::frametailgroup, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, i, WindowObjectData::markpos, WindowAggState::more_partitions, WindowAggState::next_partition, WindowAggState::numaggs, WindowAggState::numfuncs, outerPlan, outerPlanState, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, prepare_tuplestore(), WindowObjectData::seekpos, WindowAggState::spooled_rows, TupIsNull, tuplestore_puttupleslot(), unlikely, and WindowStatePerFuncData::winobj.

Referenced by ExecWindowAgg().

◆ calculate_frame_offsets()

static pg_noinline void calculate_frame_offsets ( PlanState pstate)
static

Definition at line 2082 of file nodeWindowAgg.c.

2083 {
2084  WindowAggState *winstate = castNode(WindowAggState, pstate);
2085  ExprContext *econtext;
2086  int frameOptions = winstate->frameOptions;
2087  Datum value;
2088  bool isnull;
2089  int16 len;
2090  bool byval;
2091 
2092  /* Ensure we've not been called before for this scan */
2093  Assert(winstate->all_first);
2094 
2095  econtext = winstate->ss.ps.ps_ExprContext;
2096 
2097  if (frameOptions & FRAMEOPTION_START_OFFSET)
2098  {
2099  Assert(winstate->startOffset != NULL);
2101  econtext,
2102  &isnull);
2103  if (isnull)
2104  ereport(ERROR,
2105  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2106  errmsg("frame starting offset must not be null")));
2107  /* copy value into query-lifespan context */
2108  get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
2109  &len,
2110  &byval);
2111  winstate->startOffsetValue = datumCopy(value, byval, len);
2112  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2113  {
2114  /* value is known to be int8 */
2115  int64 offset = DatumGetInt64(value);
2116 
2117  if (offset < 0)
2118  ereport(ERROR,
2119  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2120  errmsg("frame starting offset must not be negative")));
2121  }
2122  }
2123 
2124  if (frameOptions & FRAMEOPTION_END_OFFSET)
2125  {
2126  Assert(winstate->endOffset != NULL);
2128  econtext,
2129  &isnull);
2130  if (isnull)
2131  ereport(ERROR,
2132  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2133  errmsg("frame ending offset must not be null")));
2134  /* copy value into query-lifespan context */
2135  get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2136  &len,
2137  &byval);
2138  winstate->endOffsetValue = datumCopy(value, byval, len);
2139  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2140  {
2141  /* value is known to be int8 */
2142  int64 offset = DatumGetInt64(value);
2143 
2144  if (offset < 0)
2145  ereport(ERROR,
2146  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2147  errmsg("frame ending offset must not be negative")));
2148  }
2149  }
2150  winstate->all_first = false;
2151 }
int64_t int64
Definition: c.h:482
int16_t int16
Definition: c.h:480
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:366
static struct @160 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2251
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:604
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:602
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:586
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:585
const void size_t len
static int64 DatumGetInt64(Datum X)
Definition: postgres.h:385
Expr * expr
Definition: execnodes.h:111
ExprContext * ps_ExprContext
Definition: execnodes.h:1165
ExprState * endOffset
Definition: execnodes.h:2620
Datum startOffsetValue
Definition: execnodes.h:2621
Datum endOffsetValue
Definition: execnodes.h:2622
ExprState * startOffset
Definition: execnodes.h:2619

References WindowAggState::all_first, Assert, castNode, datumCopy(), DatumGetInt64(), WindowAggState::endOffset, WindowAggState::endOffsetValue, ereport, errcode(), errmsg(), ERROR, ExecEvalExprSwitchContext(), ExprState::expr, exprType(), FRAMEOPTION_END_OFFSET, FRAMEOPTION_GROUPS, FRAMEOPTION_ROWS, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, get_typlenbyval(), len, ScanState::ps, PlanState::ps_ExprContext, WindowAggState::ss, WindowAggState::startOffset, WindowAggState::startOffsetValue, and value.

Referenced by ExecWindowAgg().

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 663 of file nodeWindowAgg.c.

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

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

Referenced by ExecWindowAgg().

◆ eval_windowfunction()

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

Definition at line 1033 of file nodeWindowAgg.c.

1035 {
1036  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1037  MemoryContext oldContext;
1038 
1040 
1041  /*
1042  * We don't pass any normal arguments to a window function, but we do pass
1043  * it the number of arguments, in order to permit window function
1044  * implementations to support varying numbers of arguments. The real info
1045  * goes through the WindowObject, which is passed via fcinfo->context.
1046  */
1047  InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1048  perfuncstate->numArguments,
1049  perfuncstate->winCollation,
1050  (Node *) 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 2739 of file nodeWindowAgg.c.

2740 {
2742  int i;
2743 
2744  if (node->buffer != NULL)
2745  {
2746  tuplestore_end(node->buffer);
2747 
2748  /* nullify so that release_partition skips the tuplestore_clear() */
2749  node->buffer = NULL;
2750  }
2751 
2752  release_partition(node);
2753 
2754  for (i = 0; i < node->numaggs; i++)
2755  {
2756  if (node->peragg[i].aggcontext != node->aggcontext)
2758  }
2761 
2762  pfree(node->perfunc);
2763  pfree(node->peragg);
2764 
2765  outerPlan = outerPlanState(node);
2767 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition: execnodes.h:2649
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

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

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2431 of file nodeWindowAgg.c.

2432 {
2433  WindowAggState *winstate;
2434  Plan *outerPlan;
2435  ExprContext *econtext;
2436  ExprContext *tmpcontext;
2437  WindowStatePerFunc perfunc;
2438  WindowStatePerAgg peragg;
2439  int frameOptions = node->frameOptions;
2440  int numfuncs,
2441  wfuncno,
2442  numaggs,
2443  aggno;
2444  TupleDesc scanDesc;
2445  ListCell *l;
2446 
2447  /* check for unsupported flags */
2448  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2449 
2450  /*
2451  * create state structure
2452  */
2453  winstate = makeNode(WindowAggState);
2454  winstate->ss.ps.plan = (Plan *) node;
2455  winstate->ss.ps.state = estate;
2456  winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2457 
2458  /* copy frame options to state node for easy access */
2459  winstate->frameOptions = frameOptions;
2460 
2461  /*
2462  * Create expression contexts. We need two, one for per-input-tuple
2463  * processing and one for per-output-tuple processing. We cheat a little
2464  * by using ExecAssignExprContext() to build both.
2465  */
2466  ExecAssignExprContext(estate, &winstate->ss.ps);
2467  tmpcontext = winstate->ss.ps.ps_ExprContext;
2468  winstate->tmpcontext = tmpcontext;
2469  ExecAssignExprContext(estate, &winstate->ss.ps);
2470 
2471  /* Create long-lived context for storage of partition-local memory etc */
2472  winstate->partcontext =
2474  "WindowAgg Partition",
2476 
2477  /*
2478  * Create mid-lived context for aggregate trans values etc.
2479  *
2480  * Note that moving aggregates each use their own private context, not
2481  * this one.
2482  */
2483  winstate->aggcontext =
2485  "WindowAgg Aggregates",
2487 
2488  /* Only the top-level WindowAgg may have a qual */
2489  Assert(node->plan.qual == NIL || node->topWindow);
2490 
2491  /* Initialize the qual */
2492  winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2493  (PlanState *) winstate);
2494 
2495  /*
2496  * Setup the run condition, if we received one from the query planner.
2497  * When set, this may allow us to move into pass-through mode so that we
2498  * don't have to perform any further evaluation of WindowFuncs in the
2499  * current partition or possibly stop returning tuples altogether when all
2500  * tuples are in the same partition.
2501  */
2502  winstate->runcondition = ExecInitQual(node->runCondition,
2503  (PlanState *) winstate);
2504 
2505  /*
2506  * When we're not the top-level WindowAgg node or we are but have a
2507  * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2508  * modes when the runCondition becomes false.
2509  */
2510  winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2511 
2512  /* remember if we're the top-window or we are below the top-window */
2513  winstate->top_window = node->topWindow;
2514 
2515  /*
2516  * initialize child nodes
2517  */
2518  outerPlan = outerPlan(node);
2519  outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2520 
2521  /*
2522  * initialize source tuple type (which is also the tuple type that we'll
2523  * store in the tuplestore and use in all our working slots).
2524  */
2526  scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2527 
2528  /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2529  winstate->ss.ps.outeropsset = true;
2530  winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2531  winstate->ss.ps.outeropsfixed = true;
2532 
2533  /*
2534  * tuple table initialization
2535  */
2536  winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2538  winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2540  winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2542  winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2544 
2545  /*
2546  * create frame head and tail slots only if needed (must create slots in
2547  * exactly the same cases that update_frameheadpos and update_frametailpos
2548  * need them)
2549  */
2550  winstate->framehead_slot = winstate->frametail_slot = NULL;
2551 
2552  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2553  {
2554  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2555  node->ordNumCols != 0) ||
2556  (frameOptions & FRAMEOPTION_START_OFFSET))
2557  winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2559  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2560  node->ordNumCols != 0) ||
2561  (frameOptions & FRAMEOPTION_END_OFFSET))
2562  winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2564  }
2565 
2566  /*
2567  * Initialize result slot, type and projection.
2568  */
2570  ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2571 
2572  /* Set up data for comparing tuples */
2573  if (node->partNumCols > 0)
2574  winstate->partEqfunction =
2575  execTuplesMatchPrepare(scanDesc,
2576  node->partNumCols,
2577  node->partColIdx,
2578  node->partOperators,
2579  node->partCollations,
2580  &winstate->ss.ps);
2581 
2582  if (node->ordNumCols > 0)
2583  winstate->ordEqfunction =
2584  execTuplesMatchPrepare(scanDesc,
2585  node->ordNumCols,
2586  node->ordColIdx,
2587  node->ordOperators,
2588  node->ordCollations,
2589  &winstate->ss.ps);
2590 
2591  /*
2592  * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2593  */
2594  numfuncs = winstate->numfuncs;
2595  numaggs = winstate->numaggs;
2596  econtext = winstate->ss.ps.ps_ExprContext;
2597  econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2598  econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2599 
2600  /*
2601  * allocate per-wfunc/per-agg state information.
2602  */
2603  perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2604  peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2605  winstate->perfunc = perfunc;
2606  winstate->peragg = peragg;
2607 
2608  wfuncno = -1;
2609  aggno = -1;
2610  foreach(l, winstate->funcs)
2611  {
2612  WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2613  WindowFunc *wfunc = wfuncstate->wfunc;
2614  WindowStatePerFunc perfuncstate;
2615  AclResult aclresult;
2616  int i;
2617 
2618  if (wfunc->winref != node->winref) /* planner screwed up? */
2619  elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2620  wfunc->winref, node->winref);
2621 
2622  /* Look for a previous duplicate window function */
2623  for (i = 0; i <= wfuncno; i++)
2624  {
2625  if (equal(wfunc, perfunc[i].wfunc) &&
2626  !contain_volatile_functions((Node *) wfunc))
2627  break;
2628  }
2629  if (i <= wfuncno)
2630  {
2631  /* Found a match to an existing entry, so just mark it */
2632  wfuncstate->wfuncno = i;
2633  continue;
2634  }
2635 
2636  /* Nope, so assign a new PerAgg record */
2637  perfuncstate = &perfunc[++wfuncno];
2638 
2639  /* Mark WindowFunc state node with assigned index in the result array */
2640  wfuncstate->wfuncno = wfuncno;
2641 
2642  /* Check permission to call window function */
2643  aclresult = object_aclcheck(ProcedureRelationId, wfunc->winfnoid, GetUserId(),
2644  ACL_EXECUTE);
2645  if (aclresult != ACLCHECK_OK)
2646  aclcheck_error(aclresult, OBJECT_FUNCTION,
2647  get_func_name(wfunc->winfnoid));
2648  InvokeFunctionExecuteHook(wfunc->winfnoid);
2649 
2650  /* Fill in the perfuncstate data */
2651  perfuncstate->wfuncstate = wfuncstate;
2652  perfuncstate->wfunc = wfunc;
2653  perfuncstate->numArguments = list_length(wfuncstate->args);
2654  perfuncstate->winCollation = wfunc->inputcollid;
2655 
2656  get_typlenbyval(wfunc->wintype,
2657  &perfuncstate->resulttypeLen,
2658  &perfuncstate->resulttypeByVal);
2659 
2660  /*
2661  * If it's really just a plain aggregate function, we'll emulate the
2662  * Agg environment for it.
2663  */
2664  perfuncstate->plain_agg = wfunc->winagg;
2665  if (wfunc->winagg)
2666  {
2667  WindowStatePerAgg peraggstate;
2668 
2669  perfuncstate->aggno = ++aggno;
2670  peraggstate = &winstate->peragg[aggno];
2671  initialize_peragg(winstate, wfunc, peraggstate);
2672  peraggstate->wfuncno = wfuncno;
2673  }
2674  else
2675  {
2677 
2678  winobj->winstate = winstate;
2679  winobj->argstates = wfuncstate->args;
2680  winobj->localmem = NULL;
2681  perfuncstate->winobj = winobj;
2682 
2683  /* It's a real window function, so set up to call it. */
2684  fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2685  econtext->ecxt_per_query_memory);
2686  fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2687  }
2688  }
2689 
2690  /* Update numfuncs, numaggs to match number of unique functions found */
2691  winstate->numfuncs = wfuncno + 1;
2692  winstate->numaggs = aggno + 1;
2693 
2694  /* Set up WindowObject for aggregates, if needed */
2695  if (winstate->numaggs > 0)
2696  {
2697  WindowObject agg_winobj = makeNode(WindowObjectData);
2698 
2699  agg_winobj->winstate = winstate;
2700  agg_winobj->argstates = NIL;
2701  agg_winobj->localmem = NULL;
2702  /* make sure markptr = -1 to invalidate. It may not get used */
2703  agg_winobj->markptr = -1;
2704  agg_winobj->readptr = -1;
2705  winstate->agg_winobj = agg_winobj;
2706  }
2707 
2708  /* Set the status to running */
2709  winstate->status = WINDOWAGG_RUN;
2710 
2711  /* initialize frame bound offset expressions */
2712  winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2713  (PlanState *) winstate);
2714  winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2715  (PlanState *) winstate);
2716 
2717  /* Lookup in_range support functions if needed */
2718  if (OidIsValid(node->startInRangeFunc))
2719  fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2720  if (OidIsValid(node->endInRangeFunc))
2721  fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2722  winstate->inRangeColl = node->inRangeColl;
2723  winstate->inRangeAsc = node->inRangeAsc;
2724  winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2725 
2726  winstate->all_first = true;
2727  winstate->partition_spooled = false;
2728  winstate->more_partitions = false;
2729  winstate->next_partition = true;
2730 
2731  return winstate;
2732 }
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3810
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:537
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:224
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:138
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:58
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1918
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1886
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition: execUtils.c:661
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:485
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:540
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:2576
@ WINDOWAGG_RUN
Definition: execnodes.h:2584
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:2575
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
#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
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1608
void * palloc0(Size size)
Definition: mcxt.c:1347
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
Oid GetUserId(void)
Definition: miscinit.c:524
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
#define makeNode(_type_)
Definition: nodes.h:155
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:213
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:592
@ OBJECT_FUNCTION
Definition: parsenodes.h:2287
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:584
#define ACL_EXECUTE
Definition: parsenodes.h:83
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:265
bool outeropsset
Definition: execnodes.h:1209
const TupleTableSlotOps * outerops
Definition: execnodes.h:1201
ExprState * qual
Definition: execnodes.h:1147
bool outeropsfixed
Definition: execnodes.h:1205
EState * state
Definition: execnodes.h:1128
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1132
List * qual
Definition: plannodes.h:154
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1576
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
FmgrInfo endInRangeFunc
Definition: execnodes.h:2626
FmgrInfo startInRangeFunc
Definition: execnodes.h:2625
ExprState * runcondition
Definition: execnodes.h:2637
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2675
WindowAggStatus status
Definition: execnodes.h:2616
bool inRangeNullsFirst
Definition: execnodes.h:2629
ExprState * partEqfunction
Definition: execnodes.h:2601
bool use_pass_through
Definition: execnodes.h:2632
int partNumCols
Definition: plannodes.h:1047
Oid endInRangeFunc
Definition: plannodes.h:1091
Node * endOffset
Definition: plannodes.h:1077
bool topWindow
Definition: plannodes.h:1106
Plan plan
Definition: plannodes.h:1041
Oid inRangeColl
Definition: plannodes.h:1094
Node * startOffset
Definition: plannodes.h:1074
List * runCondition
Definition: plannodes.h:1080
Oid startInRangeFunc
Definition: plannodes.h:1088
bool inRangeAsc
Definition: plannodes.h:1097
Index winref
Definition: plannodes.h:1044
bool inRangeNullsFirst
Definition: plannodes.h:1100
int frameOptions
Definition: plannodes.h:1071
WindowFunc * wfunc
Definition: execnodes.h:883
Index winref
Definition: primnodes.h:581
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, WindowAggState::next_partition, 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 2774 of file nodeWindowAgg.c.

2775 {
2777  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2778 
2779  node->status = WINDOWAGG_RUN;
2780  node->all_first = true;
2781 
2782  /* release tuplestore et al */
2783  release_partition(node);
2784 
2785  /* release all temp tuples, but especially first_part_slot */
2789  ExecClearTuple(node->temp_slot_1);
2790  ExecClearTuple(node->temp_slot_2);
2791  if (node->framehead_slot)
2793  if (node->frametail_slot)
2795 
2796  /* Forget current wfunc values */
2797  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2798  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2799 
2800  /*
2801  * if chgParam of subnode is not null then plan will be re-scanned by
2802  * first ExecProcNode.
2803  */
2804  if (outerPlan->chgParam == NULL)
2806 }
#define MemSet(start, val, len)
Definition: c.h:974
void ExecReScan(PlanState *node)
Definition: execAmi.c:76

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

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot* ExecWindowAgg ( PlanState pstate)
static

Definition at line 2163 of file nodeWindowAgg.c.

2164 {
2165  WindowAggState *winstate = castNode(WindowAggState, pstate);
2166  TupleTableSlot *slot;
2167  ExprContext *econtext;
2168  int i;
2169  int numfuncs;
2170 
2172 
2173  if (winstate->status == WINDOWAGG_DONE)
2174  return NULL;
2175 
2176  /*
2177  * Compute frame offset values, if any, during first call (or after a
2178  * rescan). These are assumed to hold constant throughout the scan; if
2179  * user gives us a volatile expression, we'll only use its initial value.
2180  */
2181  if (unlikely(winstate->all_first))
2182  calculate_frame_offsets(pstate);
2183 
2184  /* We need to loop as the runCondition or qual may filter out tuples */
2185  for (;;)
2186  {
2187  if (winstate->next_partition)
2188  {
2189  /* Initialize for first partition and set current row = 0 */
2190  begin_partition(winstate);
2191  /* If there are no input rows, we'll detect that and exit below */
2192  }
2193  else
2194  {
2195  /* Advance current row within partition */
2196  winstate->currentpos++;
2197  /* This might mean that the frame moves, too */
2198  winstate->framehead_valid = false;
2199  winstate->frametail_valid = false;
2200  /* we don't need to invalidate grouptail here; see below */
2201  }
2202 
2203  /*
2204  * Spool all tuples up to and including the current row, if we haven't
2205  * already
2206  */
2207  spool_tuples(winstate, winstate->currentpos);
2208 
2209  /* Move to the next partition if we reached the end of this partition */
2210  if (winstate->partition_spooled &&
2211  winstate->currentpos >= winstate->spooled_rows)
2212  {
2213  release_partition(winstate);
2214 
2215  if (winstate->more_partitions)
2216  {
2217  begin_partition(winstate);
2218  Assert(winstate->spooled_rows > 0);
2219 
2220  /* Come out of pass-through mode when changing partition */
2221  winstate->status = WINDOWAGG_RUN;
2222  }
2223  else
2224  {
2225  /* No further partitions? We're done */
2226  winstate->status = WINDOWAGG_DONE;
2227  return NULL;
2228  }
2229  }
2230 
2231  /* final output execution is in ps_ExprContext */
2232  econtext = winstate->ss.ps.ps_ExprContext;
2233 
2234  /* Clear the per-output-tuple context for current row */
2235  ResetExprContext(econtext);
2236 
2237  /*
2238  * Read the current row from the tuplestore, and save in
2239  * ScanTupleSlot. (We can't rely on the outerplan's output slot
2240  * because we may have to read beyond the current row. Also, we have
2241  * to actually copy the row out of the tuplestore, since window
2242  * function evaluation might cause the tuplestore to dump its state to
2243  * disk.)
2244  *
2245  * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2246  * we must also detect entering a new peer group and update associated
2247  * state when that happens. We use temp_slot_2 to temporarily hold
2248  * the previous row for this purpose.
2249  *
2250  * Current row must be in the tuplestore, since we spooled it above.
2251  */
2252  tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
2253  if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2256  winstate->currentpos > 0)
2257  {
2258  ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2259  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2260  winstate->ss.ss_ScanTupleSlot))
2261  elog(ERROR, "unexpected end of tuplestore");
2262  if (!are_peers(winstate, winstate->temp_slot_2,
2263  winstate->ss.ss_ScanTupleSlot))
2264  {
2265  winstate->currentgroup++;
2266  winstate->groupheadpos = winstate->currentpos;
2267  winstate->grouptail_valid = false;
2268  }
2269  ExecClearTuple(winstate->temp_slot_2);
2270  }
2271  else
2272  {
2273  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2274  winstate->ss.ss_ScanTupleSlot))
2275  elog(ERROR, "unexpected end of tuplestore");
2276  }
2277 
2278  /* don't evaluate the window functions when we're in pass-through mode */
2279  if (winstate->status == WINDOWAGG_RUN)
2280  {
2281  /*
2282  * Evaluate true window functions
2283  */
2284  numfuncs = winstate->numfuncs;
2285  for (i = 0; i < numfuncs; i++)
2286  {
2287  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2288 
2289  if (perfuncstate->plain_agg)
2290  continue;
2291  eval_windowfunction(winstate, perfuncstate,
2292  &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2293  &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2294  }
2295 
2296  /*
2297  * Evaluate aggregates
2298  */
2299  if (winstate->numaggs > 0)
2300  eval_windowaggregates(winstate);
2301  }
2302 
2303  /*
2304  * If we have created auxiliary read pointers for the frame or group
2305  * boundaries, force them to be kept up-to-date, because we don't know
2306  * whether the window function(s) will do anything that requires that.
2307  * Failing to advance the pointers would result in being unable to
2308  * trim data from the tuplestore, which is bad. (If we could know in
2309  * advance whether the window functions will use frame boundary info,
2310  * we could skip creating these pointers in the first place ... but
2311  * unfortunately the window function API doesn't require that.)
2312  */
2313  if (winstate->framehead_ptr >= 0)
2314  update_frameheadpos(winstate);
2315  if (winstate->frametail_ptr >= 0)
2316  update_frametailpos(winstate);
2317  if (winstate->grouptail_ptr >= 0)
2318  update_grouptailpos(winstate);
2319 
2320  /*
2321  * Truncate any no-longer-needed rows from the tuplestore.
2322  */
2323  tuplestore_trim(winstate->buffer);
2324 
2325  /*
2326  * Form and return a projection tuple using the windowfunc results and
2327  * the current row. Setting ecxt_outertuple arranges that any Vars
2328  * will be evaluated with respect to that row.
2329  */
2330  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2331 
2332  slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2333 
2334  if (winstate->status == WINDOWAGG_RUN)
2335  {
2336  econtext->ecxt_scantuple = slot;
2337 
2338  /*
2339  * Now evaluate the run condition to see if we need to go into
2340  * pass-through mode, or maybe stop completely.
2341  */
2342  if (!ExecQual(winstate->runcondition, econtext))
2343  {
2344  /*
2345  * Determine which mode to move into. If there is no
2346  * PARTITION BY clause and we're the top-level WindowAgg then
2347  * we're done. This tuple and any future tuples cannot
2348  * possibly match the runcondition. However, when there is a
2349  * PARTITION BY clause or we're not the top-level window we
2350  * can't just stop as we need to either process other
2351  * partitions or ensure WindowAgg nodes above us receive all
2352  * of the tuples they need to process their WindowFuncs.
2353  */
2354  if (winstate->use_pass_through)
2355  {
2356  /*
2357  * When switching into a pass-through mode, we'd better
2358  * NULLify the aggregate results as these are no longer
2359  * updated and NULLifying them avoids the old stale
2360  * results lingering. Some of these might be byref types
2361  * so we can't have them pointing to free'd memory. The
2362  * planner insisted that quals used in the runcondition
2363  * are strict, so the top-level WindowAgg will always
2364  * filter these NULLs out in the filter clause.
2365  */
2366  numfuncs = winstate->numfuncs;
2367  for (i = 0; i < numfuncs; i++)
2368  {
2369  econtext->ecxt_aggvalues[i] = (Datum) 0;
2370  econtext->ecxt_aggnulls[i] = true;
2371  }
2372 
2373  /*
2374  * STRICT pass-through mode is required for the top window
2375  * when there is a PARTITION BY clause. Otherwise we must
2376  * ensure we store tuples that don't match the
2377  * runcondition so they're available to WindowAggs above.
2378  */
2379  if (winstate->top_window)
2380  {
2382  continue;
2383  }
2384  else
2385  {
2386  winstate->status = WINDOWAGG_PASSTHROUGH;
2387  }
2388  }
2389  else
2390  {
2391  /*
2392  * Pass-through not required. We can just return NULL.
2393  * Nothing else will match the runcondition.
2394  */
2395  winstate->status = WINDOWAGG_DONE;
2396  return NULL;
2397  }
2398  }
2399 
2400  /*
2401  * Filter out any tuples we don't need in the top-level WindowAgg.
2402  */
2403  if (!ExecQual(winstate->ss.ps.qual, econtext))
2404  {
2405  InstrCountFiltered1(winstate, 1);
2406  continue;
2407  }
2408 
2409  break;
2410  }
2411 
2412  /*
2413  * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2414  * we're anything apart from the top window.
2415  */
2416  else if (!winstate->top_window)
2417  break;
2418  }
2419 
2420  return slot;
2421 }
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1230
@ WINDOWAGG_PASSTHROUGH
Definition: execnodes.h:2585
@ WINDOWAGG_DONE
Definition: execnodes.h:2583
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition: execnodes.h:2586
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:394
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:431
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
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 pg_noinline void calculate_frame_offsets(PlanState *pstate)
static void eval_windowaggregates(WindowAggState *winstate)
static void update_frametailpos(WindowAggState *winstate)
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:600
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:599
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:258
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1166
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1130
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:507
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1412

References WindowAggState::all_first, are_peers(), Assert, begin_partition(), WindowAggState::buffer, calculate_frame_offsets(), castNode, CHECK_FOR_INTERRUPTS, WindowAggState::current_ptr, WindowAggState::currentgroup, WindowAggState::currentpos, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ERROR, eval_windowaggregates(), eval_windowfunction(), ExecClearTuple(), ExecCopySlot(), ExecProject(), ExecQual(), WindowAggState::framehead_ptr, WindowAggState::framehead_valid, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_valid, WindowAggState::groupheadpos, WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, i, InstrCountFiltered1, WindowAggState::more_partitions, WindowAggState::next_partition, 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::status, WindowAggState::temp_slot_2, WindowAggState::top_window, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), unlikely, update_frameheadpos(), update_frametailpos(), update_grouptailpos(), WindowAggState::use_pass_through, 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  (Node *) 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 3087 of file nodeWindowAgg.c.

3088 {
3089  Oid typinput,
3090  typioparam;
3091  char *strInitVal;
3092  Datum initVal;
3093 
3094  getTypeInputInfo(transtype, &typinput, &typioparam);
3095  strInitVal = TextDatumGetCString(textInitVal);
3096  initVal = OidInputFunctionCall(typinput, strInitVal,
3097  typioparam, -1);
3098  pfree(strInitVal);
3099  return initVal;
3100 }
#define TextDatumGetCString(d)
Definition: builtins.h:98
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1754
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
unsigned int Oid
Definition: postgres_ext.h:31

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

Referenced by initialize_peragg().

◆ initialize_peragg()

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

Definition at line 2814 of file nodeWindowAgg.c.

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

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 207 of file nodeWindowAgg.c.

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

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

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ prepare_tuplestore()

static pg_noinline void prepare_tuplestore ( WindowAggState winstate)
static

Definition at line 1085 of file nodeWindowAgg.c.

1086 {
1087  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1088  int frameOptions = winstate->frameOptions;
1089  int numfuncs = winstate->numfuncs;
1090 
1091  /* we shouldn't be called if this was done already */
1092  Assert(winstate->buffer == NULL);
1093 
1094  /* Create new tuplestore */
1095  winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1096 
1097  /*
1098  * Set up read pointers for the tuplestore. The current pointer doesn't
1099  * need BACKWARD capability, but the per-window-function read pointers do,
1100  * and the aggregate pointer does if we might need to restart aggregation.
1101  */
1102  winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1103 
1104  /* reset default REWIND capability bit for current ptr */
1105  tuplestore_set_eflags(winstate->buffer, 0);
1106 
1107  /* create read pointers for aggregates, if needed */
1108  if (winstate->numaggs > 0)
1109  {
1110  WindowObject agg_winobj = winstate->agg_winobj;
1111  int readptr_flags = 0;
1112 
1113  /*
1114  * If the frame head is potentially movable, or we have an EXCLUSION
1115  * clause, we might need to restart aggregation ...
1116  */
1117  if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1118  (frameOptions & FRAMEOPTION_EXCLUSION))
1119  {
1120  /* ... so create a mark pointer to track the frame head */
1121  agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1122  /* and the read pointer will need BACKWARD capability */
1123  readptr_flags |= EXEC_FLAG_BACKWARD;
1124  }
1125 
1126  agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1127  readptr_flags);
1128  }
1129 
1130  /* create mark and read pointers for each real window function */
1131  for (int i = 0; i < numfuncs; i++)
1132  {
1133  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1134 
1135  if (!perfuncstate->plain_agg)
1136  {
1137  WindowObject winobj = perfuncstate->winobj;
1138 
1139  winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1140  0);
1141  winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1143  }
1144  }
1145 
1146  /*
1147  * If we are in RANGE or GROUPS mode, then determining frame boundaries
1148  * requires physical access to the frame endpoint rows, except in certain
1149  * degenerate cases. We create read pointers to point to those rows, to
1150  * simplify access and ensure that the tuplestore doesn't discard the
1151  * endpoint rows prematurely. (Must create pointers in exactly the same
1152  * cases that update_frameheadpos and update_frametailpos need them.)
1153  */
1154  winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1155 
1156  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1157  {
1158  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1159  node->ordNumCols != 0) ||
1160  (frameOptions & FRAMEOPTION_START_OFFSET))
1161  winstate->framehead_ptr =
1162  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1163  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1164  node->ordNumCols != 0) ||
1165  (frameOptions & FRAMEOPTION_END_OFFSET))
1166  winstate->frametail_ptr =
1167  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1168  }
1169 
1170  /*
1171  * If we have an exclusion clause that requires knowing the boundaries of
1172  * the current row's peer group, we create a read pointer to track the
1173  * tail position of the peer group (i.e., first row of the next peer
1174  * group). The head position does not require its own pointer because we
1175  * maintain that as a side effect of advancing the current row.
1176  */
1177  winstate->grouptail_ptr = -1;
1178 
1179  if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1181  node->ordNumCols != 0)
1182  {
1183  winstate->grouptail_ptr =
1184  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1185  }
1186 }
int work_mem
Definition: globals.c:130
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:395
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:371

References WindowAggState::agg_winobj, Assert, WindowAggState::buffer, WindowAggState::current_ptr, EXEC_FLAG_BACKWARD, WindowAggState::framehead_ptr, 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::grouptail_ptr, i, WindowObjectData::markptr, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAgg::ordNumCols, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, PlanState::plan, ScanState::ps, WindowObjectData::readptr, WindowAggState::ss, tuplestore_alloc_read_pointer(), tuplestore_begin_heap(), tuplestore_set_eflags(), WindowStatePerFuncData::winobj, and work_mem.

Referenced by begin_partition().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1376 of file nodeWindowAgg.c.

1377 {
1378  int i;
1379 
1380  for (i = 0; i < winstate->numfuncs; i++)
1381  {
1382  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1383 
1384  /* Release any partition-local state of this window function */
1385  if (perfuncstate->winobj)
1386  perfuncstate->winobj->localmem = NULL;
1387  }
1388 
1389  /*
1390  * Release all partition-local memory (in particular, any partition-local
1391  * state that we might have trashed our pointers to in the above loop, and
1392  * any aggregate temp data). We don't rely on retail pfree because some
1393  * aggregates might have allocated data we don't have direct pointers to.
1394  */
1395  MemoryContextReset(winstate->partcontext);
1396  MemoryContextReset(winstate->aggcontext);
1397  for (i = 0; i < winstate->numaggs; i++)
1398  {
1399  if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1400  MemoryContextReset(winstate->peragg[i].aggcontext);
1401  }
1402 
1403  if (winstate->buffer)
1404  tuplestore_clear(winstate->buffer);
1405  winstate->partition_spooled = false;
1406  winstate->next_partition = true;
1407 }
void tuplestore_clear(Tuplestorestate *state)
Definition: tuplestore.c:430

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, i, WindowObjectData::localmem, MemoryContextReset(), WindowAggState::next_partition, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partcontext, WindowAggState::partition_spooled, WindowAggState::peragg, WindowAggState::perfunc, tuplestore_clear(), 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 1426 of file nodeWindowAgg.c.

1427 {
1428  int frameOptions = winstate->frameOptions;
1429 
1430  Assert(pos >= 0); /* else caller error */
1431 
1432  /*
1433  * First, check frame starting conditions. We might as well delegate this
1434  * to update_frameheadpos always; it doesn't add any notable cost.
1435  */
1436  update_frameheadpos(winstate);
1437  if (pos < winstate->frameheadpos)
1438  return 0;
1439 
1440  /*
1441  * Okay so far, now check frame ending conditions. Here, we avoid calling
1442  * update_frametailpos in simple cases, so as not to spool tuples further
1443  * ahead than necessary.
1444  */
1445  if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1446  {
1447  if (frameOptions & FRAMEOPTION_ROWS)
1448  {
1449  /* rows after current row are out of frame */
1450  if (pos > winstate->currentpos)
1451  return -1;
1452  }
1453  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1454  {
1455  /* following row that is not peer is out of frame */
1456  if (pos > winstate->currentpos &&
1457  !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1458  return -1;
1459  }
1460  else
1461  Assert(false);
1462  }
1463  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1464  {
1465  if (frameOptions & FRAMEOPTION_ROWS)
1466  {
1467  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1468 
1469  /* rows after current row + offset are out of frame */
1470  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1471  offset = -offset;
1472 
1473  if (pos > winstate->currentpos + offset)
1474  return -1;
1475  }
1476  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1477  {
1478  /* hard cases, so delegate to update_frametailpos */
1479  update_frametailpos(winstate);
1480  if (pos >= winstate->frametailpos)
1481  return -1;
1482  }
1483  else
1484  Assert(false);
1485  }
1486 
1487  /* Check exclusion clause */
1488  if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1489  {
1490  if (pos == winstate->currentpos)
1491  return 0;
1492  }
1493  else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1494  ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1495  pos != winstate->currentpos))
1496  {
1497  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1498 
1499  /* If no ORDER BY, all rows are peers with each other */
1500  if (node->ordNumCols == 0)
1501  return 0;
1502  /* Otherwise, check the group boundaries */
1503  if (pos >= winstate->groupheadpos)
1504  {
1505  update_grouptailpos(winstate);
1506  if (pos < winstate->grouptailpos)
1507  return 0;
1508  }
1509  }
1510 
1511  /* If we get here, it's in frame */
1512  return 1;
1513 }
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:598
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:595

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

1283 {
1284  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1286  TupleTableSlot *outerslot;
1287  MemoryContext oldcontext;
1288 
1289  if (!winstate->buffer)
1290  return; /* just a safety check */
1291  if (winstate->partition_spooled)
1292  return; /* whole partition done already */
1293 
1294  /*
1295  * When in pass-through mode we can just exhaust all tuples in the current
1296  * partition. We don't need these tuples for any further window function
1297  * evaluation, however, we do need to keep them around if we're not the
1298  * top-level window as another WindowAgg node above must see these.
1299  */
1300  if (winstate->status != WINDOWAGG_RUN)
1301  {
1302  Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1303  winstate->status == WINDOWAGG_PASSTHROUGH_STRICT);
1304 
1305  pos = -1;
1306  }
1307 
1308  /*
1309  * If the tuplestore has spilled to disk, alternate reading and writing
1310  * becomes quite expensive due to frequent buffer flushes. It's cheaper
1311  * to force the entire partition to get spooled in one go.
1312  *
1313  * XXX this is a horrid kluge --- it'd be better to fix the performance
1314  * problem inside tuplestore. FIXME
1315  */
1316  else if (!tuplestore_in_memory(winstate->buffer))
1317  pos = -1;
1318 
1319  outerPlan = outerPlanState(winstate);
1320 
1321  /* Must be in query context to call outerplan */
1323 
1324  while (winstate->spooled_rows <= pos || pos == -1)
1325  {
1326  outerslot = ExecProcNode(outerPlan);
1327  if (TupIsNull(outerslot))
1328  {
1329  /* reached the end of the last partition */
1330  winstate->partition_spooled = true;
1331  winstate->more_partitions = false;
1332  break;
1333  }
1334 
1335  if (node->partNumCols > 0)
1336  {
1337  ExprContext *econtext = winstate->tmpcontext;
1338 
1339  econtext->ecxt_innertuple = winstate->first_part_slot;
1340  econtext->ecxt_outertuple = outerslot;
1341 
1342  /* Check if this tuple still belongs to the current partition */
1343  if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1344  {
1345  /*
1346  * end of partition; copy the tuple for the next cycle.
1347  */
1348  ExecCopySlot(winstate->first_part_slot, outerslot);
1349  winstate->partition_spooled = true;
1350  winstate->more_partitions = true;
1351  break;
1352  }
1353  }
1354 
1355  /*
1356  * Remember the tuple unless we're the top-level window and we're in
1357  * pass-through mode.
1358  */
1359  if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1360  {
1361  /* Still in partition, so save it into the tuplestore */
1362  tuplestore_puttupleslot(winstate->buffer, outerslot);
1363  winstate->spooled_rows++;
1364  }
1365  }
1366 
1367  MemoryContextSwitchTo(oldcontext);
1368 }
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:260
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1554

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

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

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

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

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1776 of file nodeWindowAgg.c.

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

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

2027 {
2028  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2029  MemoryContext oldcontext;
2030 
2031  if (winstate->grouptail_valid)
2032  return; /* already known for current row */
2033 
2034  /* We may be called in a short-lived context */
2036 
2037  /* If no ORDER BY, all rows are peers with each other */
2038  if (node->ordNumCols == 0)
2039  {
2040  spool_tuples(winstate, -1);
2041  winstate->grouptailpos = winstate->spooled_rows;
2042  winstate->grouptail_valid = true;
2043  MemoryContextSwitchTo(oldcontext);
2044  return;
2045  }
2046 
2047  /*
2048  * Because grouptail_valid is reset only when current row advances into a
2049  * new peer group, we always reach here knowing that grouptailpos needs to
2050  * be advanced by at least one row. Hence, unlike the otherwise similar
2051  * case for frame tail tracking, we do not need persistent storage of the
2052  * group tail row.
2053  */
2054  Assert(winstate->grouptailpos <= winstate->currentpos);
2056  winstate->grouptail_ptr);
2057  for (;;)
2058  {
2059  /* Note we advance grouptailpos even if the fetch fails */
2060  winstate->grouptailpos++;
2061  spool_tuples(winstate, winstate->grouptailpos);
2062  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2063  winstate->temp_slot_2))
2064  break; /* end of partition */
2065  if (winstate->grouptailpos > winstate->currentpos &&
2066  !are_peers(winstate, winstate->temp_slot_2,
2067  winstate->ss.ss_ScanTupleSlot))
2068  break; /* this row is the group tail */
2069  }
2070  ExecClearTuple(winstate->temp_slot_2);
2071  winstate->grouptail_valid = true;
2072 
2073  MemoryContextSwitchTo(oldcontext);
2074 }

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

3133 {
3134  WindowAggState *winstate = winobj->winstate;
3135  MemoryContext oldcontext;
3136 
3137  /* often called repeatedly in a row */
3139 
3140  /* Don't allow passing -1 to spool_tuples here */
3141  if (pos < 0)
3142  return false;
3143 
3144  /* If necessary, fetch the tuple into the spool */
3145  spool_tuples(winstate, pos);
3146 
3147  if (pos >= winstate->spooled_rows)
3148  return false;
3149 
3150  if (pos < winobj->markpos)
3151  elog(ERROR, "cannot fetch row before WindowObject's mark position");
3152 
3154 
3155  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3156 
3157  /*
3158  * Advance or rewind until we are within one tuple of the one we want.
3159  */
3160  if (winobj->seekpos < pos - 1)
3161  {
3162  if (!tuplestore_skiptuples(winstate->buffer,
3163  pos - 1 - winobj->seekpos,
3164  true))
3165  elog(ERROR, "unexpected end of tuplestore");
3166  winobj->seekpos = pos - 1;
3167  }
3168  else if (winobj->seekpos > pos + 1)
3169  {
3170  if (!tuplestore_skiptuples(winstate->buffer,
3171  winobj->seekpos - (pos + 1),
3172  false))
3173  elog(ERROR, "unexpected end of tuplestore");
3174  winobj->seekpos = pos + 1;
3175  }
3176  else if (winobj->seekpos == pos)
3177  {
3178  /*
3179  * There's no API to refetch the tuple at the current position. We
3180  * have to move one tuple forward, and then one backward. (We don't
3181  * do it the other way because we might try to fetch the row before
3182  * our mark, which isn't allowed.) XXX this case could stand to be
3183  * optimized.
3184  */
3185  tuplestore_advance(winstate->buffer, true);
3186  winobj->seekpos++;
3187  }
3188 
3189  /*
3190  * Now we should be on the tuple immediately before or after the one we
3191  * want, so just fetch forwards or backwards as appropriate.
3192  *
3193  * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3194  * the fetched tuple. This ensures that the slot's contents remain valid
3195  * through manipulations of the tuplestore, which some callers depend on.
3196  */
3197  if (winobj->seekpos > pos)
3198  {
3199  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3200  elog(ERROR, "unexpected end of tuplestore");
3201  winobj->seekpos--;
3202  }
3203  else
3204  {
3205  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3206  elog(ERROR, "unexpected end of tuplestore");
3207  winobj->seekpos++;
3208  }
3209 
3210  Assert(winobj->seekpos == pos);
3211 
3212  MemoryContextSwitchTo(oldcontext);
3213 
3214  return true;
3215 }
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1162
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1187

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

3252 {
3253  Assert(WindowObjectIsValid(winobj));
3254  return winobj->winstate->currentpos;
3255 }
#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 3659 of file nodeWindowAgg.c.

3660 {
3661  WindowAggState *winstate;
3662  ExprContext *econtext;
3663 
3664  Assert(WindowObjectIsValid(winobj));
3665  winstate = winobj->winstate;
3666 
3667  econtext = winstate->ss.ps.ps_ExprContext;
3668 
3669  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3670  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3671  econtext, isnull);
3672 }
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 3464 of file nodeWindowAgg.c.

3467 {
3468  WindowAggState *winstate;
3469  ExprContext *econtext;
3470  TupleTableSlot *slot;
3471  int64 abs_pos;
3472  int64 mark_pos;
3473 
3474  Assert(WindowObjectIsValid(winobj));
3475  winstate = winobj->winstate;
3476  econtext = winstate->ss.ps.ps_ExprContext;
3477  slot = winstate->temp_slot_1;
3478 
3479  switch (seektype)
3480  {
3481  case WINDOW_SEEK_CURRENT:
3482  elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3483  abs_pos = mark_pos = 0; /* keep compiler quiet */
3484  break;
3485  case WINDOW_SEEK_HEAD:
3486  /* rejecting relpos < 0 is easy and simplifies code below */
3487  if (relpos < 0)
3488  goto out_of_frame;
3489  update_frameheadpos(winstate);
3490  abs_pos = winstate->frameheadpos + relpos;
3491  mark_pos = abs_pos;
3492 
3493  /*
3494  * Account for exclusion option if one is active, but advance only
3495  * abs_pos not mark_pos. This prevents changes of the current
3496  * row's peer group from resulting in trying to fetch a row before
3497  * some previous mark position.
3498  *
3499  * Note that in some corner cases such as current row being
3500  * outside frame, these calculations are theoretically too simple,
3501  * but it doesn't matter because we'll end up deciding the row is
3502  * out of frame. We do not attempt to avoid fetching rows past
3503  * end of frame; that would happen in some cases anyway.
3504  */
3505  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3506  {
3507  case 0:
3508  /* no adjustment needed */
3509  break;
3511  if (abs_pos >= winstate->currentpos &&
3512  winstate->currentpos >= winstate->frameheadpos)
3513  abs_pos++;
3514  break;
3516  update_grouptailpos(winstate);
3517  if (abs_pos >= winstate->groupheadpos &&
3518  winstate->grouptailpos > winstate->frameheadpos)
3519  {
3520  int64 overlapstart = Max(winstate->groupheadpos,
3521  winstate->frameheadpos);
3522 
3523  abs_pos += winstate->grouptailpos - overlapstart;
3524  }
3525  break;
3527  update_grouptailpos(winstate);
3528  if (abs_pos >= winstate->groupheadpos &&
3529  winstate->grouptailpos > winstate->frameheadpos)
3530  {
3531  int64 overlapstart = Max(winstate->groupheadpos,
3532  winstate->frameheadpos);
3533 
3534  if (abs_pos == overlapstart)
3535  abs_pos = winstate->currentpos;
3536  else
3537  abs_pos += winstate->grouptailpos - overlapstart - 1;
3538  }
3539  break;
3540  default:
3541  elog(ERROR, "unrecognized frame option state: 0x%x",
3542  winstate->frameOptions);
3543  break;
3544  }
3545  break;
3546  case WINDOW_SEEK_TAIL:
3547  /* rejecting relpos > 0 is easy and simplifies code below */
3548  if (relpos > 0)
3549  goto out_of_frame;
3550  update_frametailpos(winstate);
3551  abs_pos = winstate->frametailpos - 1 + relpos;
3552 
3553  /*
3554  * Account for exclusion option if one is active. If there is no
3555  * exclusion, we can safely set the mark at the accessed row. But
3556  * if there is, we can only mark the frame start, because we can't
3557  * be sure how far back in the frame the exclusion might cause us
3558  * to fetch in future. Furthermore, we have to actually check
3559  * against frameheadpos here, since it's unsafe to try to fetch a
3560  * row before frame start if the mark might be there already.
3561  */
3562  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3563  {
3564  case 0:
3565  /* no adjustment needed */
3566  mark_pos = abs_pos;
3567  break;
3569  if (abs_pos <= winstate->currentpos &&
3570  winstate->currentpos < winstate->frametailpos)
3571  abs_pos--;
3572  update_frameheadpos(winstate);
3573  if (abs_pos < winstate->frameheadpos)
3574  goto out_of_frame;
3575  mark_pos = winstate->frameheadpos;
3576  break;
3578  update_grouptailpos(winstate);
3579  if (abs_pos < winstate->grouptailpos &&
3580  winstate->groupheadpos < winstate->frametailpos)
3581  {
3582  int64 overlapend = Min(winstate->grouptailpos,
3583  winstate->frametailpos);
3584 
3585  abs_pos -= overlapend - winstate->groupheadpos;
3586  }
3587  update_frameheadpos(winstate);
3588  if (abs_pos < winstate->frameheadpos)
3589  goto out_of_frame;
3590  mark_pos = winstate->frameheadpos;
3591  break;
3593  update_grouptailpos(winstate);
3594  if (abs_pos < winstate->grouptailpos &&
3595  winstate->groupheadpos < winstate->frametailpos)
3596  {
3597  int64 overlapend = Min(winstate->grouptailpos,
3598  winstate->frametailpos);
3599 
3600  if (abs_pos == overlapend - 1)
3601  abs_pos = winstate->currentpos;
3602  else
3603  abs_pos -= overlapend - 1 - winstate->groupheadpos;
3604  }
3605  update_frameheadpos(winstate);
3606  if (abs_pos < winstate->frameheadpos)
3607  goto out_of_frame;
3608  mark_pos = winstate->frameheadpos;
3609  break;
3610  default:
3611  elog(ERROR, "unrecognized frame option state: 0x%x",
3612  winstate->frameOptions);
3613  mark_pos = 0; /* keep compiler quiet */
3614  break;
3615  }
3616  break;
3617  default:
3618  elog(ERROR, "unrecognized window seek type: %d", seektype);
3619  abs_pos = mark_pos = 0; /* keep compiler quiet */
3620  break;
3621  }
3622 
3623  if (!window_gettupleslot(winobj, abs_pos, slot))
3624  goto out_of_frame;
3625 
3626  /* The code above does not detect all out-of-frame cases, so check */
3627  if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3628  goto out_of_frame;
3629 
3630  if (isout)
3631  *isout = false;
3632  if (set_mark)
3633  WinSetMarkPosition(winobj, mark_pos);
3634  econtext->ecxt_outertuple = slot;
3635  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3636  econtext, isnull);
3637 
3638 out_of_frame:
3639  if (isout)
3640  *isout = true;
3641  *isnull = true;
3642  return (Datum) 0;
3643 }
#define Min(x, y)
Definition: c.h:958
#define Max(x, y)
Definition: c.h:952
#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 3376 of file nodeWindowAgg.c.

3379 {
3380  WindowAggState *winstate;
3381  ExprContext *econtext;
3382  TupleTableSlot *slot;
3383  bool gottuple;
3384  int64 abs_pos;
3385 
3386  Assert(WindowObjectIsValid(winobj));
3387  winstate = winobj->winstate;
3388  econtext = winstate->ss.ps.ps_ExprContext;
3389  slot = winstate->temp_slot_1;
3390 
3391  switch (seektype)
3392  {
3393  case WINDOW_SEEK_CURRENT:
3394  abs_pos = winstate->currentpos + relpos;
3395  break;
3396  case WINDOW_SEEK_HEAD:
3397  abs_pos = relpos;
3398  break;
3399  case WINDOW_SEEK_TAIL:
3400  spool_tuples(winstate, -1);
3401  abs_pos = winstate->spooled_rows - 1 + relpos;
3402  break;
3403  default:
3404  elog(ERROR, "unrecognized window seek type: %d", seektype);
3405  abs_pos = 0; /* keep compiler quiet */
3406  break;
3407  }
3408 
3409  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3410 
3411  if (!gottuple)
3412  {
3413  if (isout)
3414  *isout = true;
3415  *isnull = true;
3416  return (Datum) 0;
3417  }
3418  else
3419  {
3420  if (isout)
3421  *isout = false;
3422  if (set_mark)
3423  WinSetMarkPosition(winobj, abs_pos);
3424  econtext->ecxt_outertuple = slot;
3425  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3426  econtext, isnull);
3427  }
3428 }

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

3237 {
3238  Assert(WindowObjectIsValid(winobj));
3239  if (winobj->localmem == NULL)
3240  winobj->localmem =
3242  return winobj->localmem;
3243 }
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215

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

3267 {
3268  Assert(WindowObjectIsValid(winobj));
3269  spool_tuples(winobj->winstate, -1);
3270  return winobj->winstate->spooled_rows;
3271 }

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

3320 {
3321  WindowAggState *winstate;
3322  WindowAgg *node;
3323  TupleTableSlot *slot1;
3324  TupleTableSlot *slot2;
3325  bool res;
3326 
3327  Assert(WindowObjectIsValid(winobj));
3328  winstate = winobj->winstate;
3329  node = (WindowAgg *) winstate->ss.ps.plan;
3330 
3331  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3332  if (node->ordNumCols == 0)
3333  return true;
3334 
3335  /*
3336  * Note: OK to use temp_slot_2 here because we aren't calling any
3337  * frame-related functions (those tend to clobber temp_slot_2).
3338  */
3339  slot1 = winstate->temp_slot_1;
3340  slot2 = winstate->temp_slot_2;
3341 
3342  if (!window_gettupleslot(winobj, pos1, slot1))
3343  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3344  pos1);
3345  if (!window_gettupleslot(winobj, pos2, slot2))
3346  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3347  pos2);
3348 
3349  res = are_peers(winstate, slot1, slot2);
3350 
3351  ExecClearTuple(slot1);
3352  ExecClearTuple(slot2);
3353 
3354  return res;
3355 }
#define INT64_FORMAT
Definition: c.h:503

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

3285 {
3286  WindowAggState *winstate;
3287 
3288  Assert(WindowObjectIsValid(winobj));
3289  winstate = winobj->winstate;
3290 
3291  if (markpos < winobj->markpos)
3292  elog(ERROR, "cannot move WindowObject's mark position backward");
3293  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3294  if (markpos > winobj->markpos)
3295  {
3296  tuplestore_skiptuples(winstate->buffer,
3297  markpos - winobj->markpos,
3298  true);
3299  winobj->markpos = markpos;
3300  }
3301  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3302  if (markpos > winobj->seekpos)
3303  {
3304  tuplestore_skiptuples(winstate->buffer,
3305  markpos - winobj->seekpos,
3306  true);
3307  winobj->seekpos = markpos;
3308  }
3309 }

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