PostgreSQL Source Code  git master
nodeWindowAgg.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nodeWindowAgg.c
4  * routines to handle WindowAgg nodes.
5  *
6  * A WindowAgg node evaluates "window functions" across suitable partitions
7  * of the input tuple set. Any one WindowAgg works for just a single window
8  * specification, though it can evaluate multiple window functions sharing
9  * identical window specifications. The input tuples are required to be
10  * delivered in sorted order, with the PARTITION BY columns (if any) as
11  * major sort keys and the ORDER BY columns (if any) as minor sort keys.
12  * (The planner generates a stack of WindowAggs with intervening Sort nodes
13  * as needed, if a query involves more than one window specification.)
14  *
15  * Since window functions can require access to any or all of the rows in
16  * the current partition, we accumulate rows of the partition into a
17  * tuplestore. The window functions are called using the WindowObject API
18  * so that they can access those rows as needed.
19  *
20  * We also support using plain aggregate functions as window functions.
21  * For these, the regular Agg-node environment is emulated for each partition.
22  * As required by the SQL spec, the output represents the value of the
23  * aggregate function over all rows in the current row's window frame.
24  *
25  *
26  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
27  * Portions Copyright (c) 1994, Regents of the University of California
28  *
29  * IDENTIFICATION
30  * src/backend/executor/nodeWindowAgg.c
31  *
32  *-------------------------------------------------------------------------
33  */
34 #include "postgres.h"
35 
36 #include "access/htup_details.h"
37 #include "catalog/objectaccess.h"
38 #include "catalog/pg_aggregate.h"
39 #include "catalog/pg_proc.h"
40 #include "executor/executor.h"
41 #include "executor/nodeWindowAgg.h"
42 #include "miscadmin.h"
43 #include "nodes/nodeFuncs.h"
44 #include "optimizer/optimizer.h"
45 #include "parser/parse_agg.h"
46 #include "parser/parse_coerce.h"
47 #include "utils/acl.h"
48 #include "utils/builtins.h"
49 #include "utils/datum.h"
50 #include "utils/expandeddatum.h"
51 #include "utils/lsyscache.h"
52 #include "utils/memutils.h"
53 #include "utils/regproc.h"
54 #include "utils/syscache.h"
55 #include "windowapi.h"
56 
57 /*
58  * All the window function APIs are called with this object, which is passed
59  * to window functions as fcinfo->context.
60  */
61 typedef struct WindowObjectData
62 {
64  WindowAggState *winstate; /* parent WindowAggState */
65  List *argstates; /* ExprState trees for fn's arguments */
66  void *localmem; /* WinGetPartitionLocalMemory's chunk */
67  int markptr; /* tuplestore mark pointer for this fn */
68  int readptr; /* tuplestore read pointer for this fn */
69  int64 markpos; /* row that markptr is positioned on */
70  int64 seekpos; /* row that readptr is positioned on */
72 
73 /*
74  * We have one WindowStatePerFunc struct for each window function and
75  * window aggregate handled by this node.
76  */
77 typedef struct WindowStatePerFuncData
78 {
79  /* Links to WindowFunc expr and state nodes this working state is for */
82 
83  int numArguments; /* number of arguments */
84 
85  FmgrInfo flinfo; /* fmgr lookup data for window function */
86 
87  Oid winCollation; /* collation derived for window function */
88 
89  /*
90  * We need the len and byval info for the result of each function in order
91  * to know how to copy/delete values.
92  */
95 
96  bool plain_agg; /* is it just a plain aggregate function? */
97  int aggno; /* if so, index of its WindowStatePerAggData */
98 
99  WindowObject winobj; /* object used in window function API */
101 
102 /*
103  * For plain aggregate window functions, we also have one of these.
104  */
105 typedef struct WindowStatePerAggData
106 {
107  /* Oids of transition functions */
109  Oid invtransfn_oid; /* may be InvalidOid */
110  Oid finalfn_oid; /* may be InvalidOid */
111 
112  /*
113  * fmgr lookup data for transition functions --- only valid when
114  * corresponding oid is not InvalidOid. Note in particular that fn_strict
115  * flags are kept here.
116  */
120 
121  int numFinalArgs; /* number of arguments to pass to finalfn */
122 
123  /*
124  * initial value from pg_aggregate entry
125  */
128 
129  /*
130  * cached value for current frame boundaries
131  */
134 
135  /*
136  * We need the len and byval info for the agg's input, result, and
137  * transition data types in order to know how to copy/delete values.
138  */
145 
146  int wfuncno; /* index of associated WindowStatePerFuncData */
147 
148  /* Context holding transition value and possibly other subsidiary data */
149  MemoryContext aggcontext; /* may be private, or winstate->aggcontext */
150 
151  /* Current transition value */
152  Datum transValue; /* current transition value */
154 
155  int64 transValueCount; /* number of currently-aggregated rows */
156 
157  /* Data local to eval_windowaggregates() */
158  bool restart; /* need to restart this agg in this cycle? */
160 
161 static void initialize_windowaggregate(WindowAggState *winstate,
162  WindowStatePerFunc perfuncstate,
163  WindowStatePerAgg peraggstate);
164 static void advance_windowaggregate(WindowAggState *winstate,
165  WindowStatePerFunc perfuncstate,
166  WindowStatePerAgg peraggstate);
167 static bool advance_windowaggregate_base(WindowAggState *winstate,
168  WindowStatePerFunc perfuncstate,
169  WindowStatePerAgg peraggstate);
170 static void finalize_windowaggregate(WindowAggState *winstate,
171  WindowStatePerFunc perfuncstate,
172  WindowStatePerAgg peraggstate,
173  Datum *result, bool *isnull);
174 
175 static void eval_windowaggregates(WindowAggState *winstate);
176 static void eval_windowfunction(WindowAggState *winstate,
177  WindowStatePerFunc perfuncstate,
178  Datum *result, bool *isnull);
179 
180 static void begin_partition(WindowAggState *winstate);
181 static void spool_tuples(WindowAggState *winstate, int64 pos);
182 static void release_partition(WindowAggState *winstate);
183 
184 static int row_is_in_frame(WindowAggState *winstate, int64 pos,
185  TupleTableSlot *slot);
186 static void update_frameheadpos(WindowAggState *winstate);
187 static void update_frametailpos(WindowAggState *winstate);
188 static void update_grouptailpos(WindowAggState *winstate);
189 
191  WindowFunc *wfunc,
192  WindowStatePerAgg peraggstate);
193 static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
194 
195 static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
196  TupleTableSlot *slot2);
197 static bool window_gettupleslot(WindowObject winobj, int64 pos,
198  TupleTableSlot *slot);
199 
200 
201 /*
202  * initialize_windowaggregate
203  * parallel to initialize_aggregates in nodeAgg.c
204  */
205 static void
207  WindowStatePerFunc perfuncstate,
208  WindowStatePerAgg peraggstate)
209 {
210  MemoryContext oldContext;
211 
212  /*
213  * If we're using a private aggcontext, we may reset it here. But if the
214  * context is shared, we don't know which other aggregates may still need
215  * it, so we must leave it to the caller to reset at an appropriate time.
216  */
217  if (peraggstate->aggcontext != winstate->aggcontext)
219 
220  if (peraggstate->initValueIsNull)
221  peraggstate->transValue = peraggstate->initValue;
222  else
223  {
224  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
225  peraggstate->transValue = datumCopy(peraggstate->initValue,
226  peraggstate->transtypeByVal,
227  peraggstate->transtypeLen);
228  MemoryContextSwitchTo(oldContext);
229  }
230  peraggstate->transValueIsNull = peraggstate->initValueIsNull;
231  peraggstate->transValueCount = 0;
232  peraggstate->resultValue = (Datum) 0;
233  peraggstate->resultValueIsNull = true;
234 }
235 
236 /*
237  * advance_windowaggregate
238  * parallel to advance_aggregates in nodeAgg.c
239  */
240 static void
242  WindowStatePerFunc perfuncstate,
243  WindowStatePerAgg peraggstate)
244 {
245  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
246  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
247  int numArguments = perfuncstate->numArguments;
248  Datum newVal;
249  ListCell *arg;
250  int i;
251  MemoryContext oldContext;
252  ExprContext *econtext = winstate->tmpcontext;
253  ExprState *filter = wfuncstate->aggfilter;
254 
255  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
256 
257  /* Skip anything FILTERed out */
258  if (filter)
259  {
260  bool isnull;
261  Datum res = ExecEvalExpr(filter, econtext, &isnull);
262 
263  if (isnull || !DatumGetBool(res))
264  {
265  MemoryContextSwitchTo(oldContext);
266  return;
267  }
268  }
269 
270  /* We start from 1, since the 0th arg will be the transition value */
271  i = 1;
272  foreach(arg, wfuncstate->args)
273  {
274  ExprState *argstate = (ExprState *) lfirst(arg);
275 
276  fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
277  &fcinfo->args[i].isnull);
278  i++;
279  }
280 
281  if (peraggstate->transfn.fn_strict)
282  {
283  /*
284  * For a strict transfn, nothing happens when there's a NULL input; we
285  * just keep the prior transValue. Note transValueCount doesn't
286  * change either.
287  */
288  for (i = 1; i <= numArguments; i++)
289  {
290  if (fcinfo->args[i].isnull)
291  {
292  MemoryContextSwitchTo(oldContext);
293  return;
294  }
295  }
296 
297  /*
298  * For strict transition functions with initial value NULL we use the
299  * first non-NULL input as the initial state. (We already checked
300  * that the agg's input type is binary-compatible with its transtype,
301  * so straight copy here is OK.)
302  *
303  * We must copy the datum into aggcontext if it is pass-by-ref. We do
304  * not need to pfree the old transValue, since it's NULL.
305  */
306  if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
307  {
308  MemoryContextSwitchTo(peraggstate->aggcontext);
309  peraggstate->transValue = datumCopy(fcinfo->args[1].value,
310  peraggstate->transtypeByVal,
311  peraggstate->transtypeLen);
312  peraggstate->transValueIsNull = false;
313  peraggstate->transValueCount = 1;
314  MemoryContextSwitchTo(oldContext);
315  return;
316  }
317 
318  if (peraggstate->transValueIsNull)
319  {
320  /*
321  * Don't call a strict function with NULL inputs. Note it is
322  * possible to get here despite the above tests, if the transfn is
323  * strict *and* returned a NULL on a prior cycle. If that happens
324  * we will propagate the NULL all the way to the end. That can
325  * only happen if there's no inverse transition function, though,
326  * since we disallow transitions back to NULL when there is one.
327  */
328  MemoryContextSwitchTo(oldContext);
329  Assert(!OidIsValid(peraggstate->invtransfn_oid));
330  return;
331  }
332  }
333 
334  /*
335  * OK to call the transition function. Set winstate->curaggcontext while
336  * calling it, for possible use by AggCheckCallContext.
337  */
338  InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
339  numArguments + 1,
340  perfuncstate->winCollation,
341  (void *) winstate, NULL);
342  fcinfo->args[0].value = peraggstate->transValue;
343  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
344  winstate->curaggcontext = peraggstate->aggcontext;
345  newVal = FunctionCallInvoke(fcinfo);
346  winstate->curaggcontext = NULL;
347 
348  /*
349  * Moving-aggregate transition functions must not return null, see
350  * advance_windowaggregate_base().
351  */
352  if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
353  ereport(ERROR,
354  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
355  errmsg("moving-aggregate transition function must not return null")));
356 
357  /*
358  * We must track the number of rows included in transValue, since to
359  * remove the last input, advance_windowaggregate_base() mustn't call the
360  * inverse transition function, but simply reset transValue back to its
361  * initial value.
362  */
363  peraggstate->transValueCount++;
364 
365  /*
366  * If pass-by-ref datatype, must copy the new value into aggcontext and
367  * free the prior transValue. But if transfn returned a pointer to its
368  * first input, we don't need to do anything. Also, if transfn returned a
369  * pointer to a R/W expanded object that is already a child of the
370  * aggcontext, assume we can adopt that value without copying it.
371  */
372  if (!peraggstate->transtypeByVal &&
373  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
374  {
375  if (!fcinfo->isnull)
376  {
377  MemoryContextSwitchTo(peraggstate->aggcontext);
379  false,
380  peraggstate->transtypeLen) &&
382  /* do nothing */ ;
383  else
384  newVal = datumCopy(newVal,
385  peraggstate->transtypeByVal,
386  peraggstate->transtypeLen);
387  }
388  if (!peraggstate->transValueIsNull)
389  {
391  false,
392  peraggstate->transtypeLen))
393  DeleteExpandedObject(peraggstate->transValue);
394  else
395  pfree(DatumGetPointer(peraggstate->transValue));
396  }
397  }
398 
399  MemoryContextSwitchTo(oldContext);
400  peraggstate->transValue = newVal;
401  peraggstate->transValueIsNull = fcinfo->isnull;
402 }
403 
404 /*
405  * advance_windowaggregate_base
406  * Remove the oldest tuple from an aggregation.
407  *
408  * This is very much like advance_windowaggregate, except that we will call
409  * the inverse transition function (which caller must have checked is
410  * available).
411  *
412  * Returns true if we successfully removed the current row from this
413  * aggregate, false if not (in the latter case, caller is responsible
414  * for cleaning up by restarting the aggregation).
415  */
416 static bool
418  WindowStatePerFunc perfuncstate,
419  WindowStatePerAgg peraggstate)
420 {
421  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
422  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
423  int numArguments = perfuncstate->numArguments;
424  Datum newVal;
425  ListCell *arg;
426  int i;
427  MemoryContext oldContext;
428  ExprContext *econtext = winstate->tmpcontext;
429  ExprState *filter = wfuncstate->aggfilter;
430 
431  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
432 
433  /* Skip anything FILTERed out */
434  if (filter)
435  {
436  bool isnull;
437  Datum res = ExecEvalExpr(filter, econtext, &isnull);
438 
439  if (isnull || !DatumGetBool(res))
440  {
441  MemoryContextSwitchTo(oldContext);
442  return true;
443  }
444  }
445 
446  /* We start from 1, since the 0th arg will be the transition value */
447  i = 1;
448  foreach(arg, wfuncstate->args)
449  {
450  ExprState *argstate = (ExprState *) lfirst(arg);
451 
452  fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
453  &fcinfo->args[i].isnull);
454  i++;
455  }
456 
457  if (peraggstate->invtransfn.fn_strict)
458  {
459  /*
460  * For a strict (inv)transfn, nothing happens when there's a NULL
461  * input; we just keep the prior transValue. Note transValueCount
462  * doesn't change either.
463  */
464  for (i = 1; i <= numArguments; i++)
465  {
466  if (fcinfo->args[i].isnull)
467  {
468  MemoryContextSwitchTo(oldContext);
469  return true;
470  }
471  }
472  }
473 
474  /* There should still be an added but not yet removed value */
475  Assert(peraggstate->transValueCount > 0);
476 
477  /*
478  * In moving-aggregate mode, the state must never be NULL, except possibly
479  * before any rows have been aggregated (which is surely not the case at
480  * this point). This restriction allows us to interpret a NULL result
481  * from the inverse function as meaning "sorry, can't do an inverse
482  * transition in this case". We already checked this in
483  * advance_windowaggregate, but just for safety, check again.
484  */
485  if (peraggstate->transValueIsNull)
486  elog(ERROR, "aggregate transition value is NULL before inverse transition");
487 
488  /*
489  * We mustn't use the inverse transition function to remove the last
490  * input. Doing so would yield a non-NULL state, whereas we should be in
491  * the initial state afterwards which may very well be NULL. So instead,
492  * we simply re-initialize the aggregate in this case.
493  */
494  if (peraggstate->transValueCount == 1)
495  {
496  MemoryContextSwitchTo(oldContext);
498  &winstate->perfunc[peraggstate->wfuncno],
499  peraggstate);
500  return true;
501  }
502 
503  /*
504  * OK to call the inverse transition function. Set
505  * winstate->curaggcontext while calling it, for possible use by
506  * AggCheckCallContext.
507  */
508  InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
509  numArguments + 1,
510  perfuncstate->winCollation,
511  (void *) winstate, NULL);
512  fcinfo->args[0].value = peraggstate->transValue;
513  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
514  winstate->curaggcontext = peraggstate->aggcontext;
515  newVal = FunctionCallInvoke(fcinfo);
516  winstate->curaggcontext = NULL;
517 
518  /*
519  * If the function returns NULL, report failure, forcing a restart.
520  */
521  if (fcinfo->isnull)
522  {
523  MemoryContextSwitchTo(oldContext);
524  return false;
525  }
526 
527  /* Update number of rows included in transValue */
528  peraggstate->transValueCount--;
529 
530  /*
531  * If pass-by-ref datatype, must copy the new value into aggcontext and
532  * free the prior transValue. But if invtransfn returned a pointer to its
533  * first input, we don't need to do anything. Also, if invtransfn
534  * returned a pointer to a R/W expanded object that is already a child of
535  * the aggcontext, assume we can adopt that value without copying it.
536  *
537  * Note: the checks for null values here will never fire, but it seems
538  * best to have this stanza look just like advance_windowaggregate.
539  */
540  if (!peraggstate->transtypeByVal &&
541  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
542  {
543  if (!fcinfo->isnull)
544  {
545  MemoryContextSwitchTo(peraggstate->aggcontext);
547  false,
548  peraggstate->transtypeLen) &&
550  /* do nothing */ ;
551  else
552  newVal = datumCopy(newVal,
553  peraggstate->transtypeByVal,
554  peraggstate->transtypeLen);
555  }
556  if (!peraggstate->transValueIsNull)
557  {
559  false,
560  peraggstate->transtypeLen))
561  DeleteExpandedObject(peraggstate->transValue);
562  else
563  pfree(DatumGetPointer(peraggstate->transValue));
564  }
565  }
566 
567  MemoryContextSwitchTo(oldContext);
568  peraggstate->transValue = newVal;
569  peraggstate->transValueIsNull = fcinfo->isnull;
570 
571  return true;
572 }
573 
574 /*
575  * finalize_windowaggregate
576  * parallel to finalize_aggregate in nodeAgg.c
577  */
578 static void
580  WindowStatePerFunc perfuncstate,
581  WindowStatePerAgg peraggstate,
582  Datum *result, bool *isnull)
583 {
584  MemoryContext oldContext;
585 
587 
588  /*
589  * Apply the agg's finalfn if one is provided, else return transValue.
590  */
591  if (OidIsValid(peraggstate->finalfn_oid))
592  {
593  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
594  int numFinalArgs = peraggstate->numFinalArgs;
595  bool anynull;
596  int i;
597 
598  InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
599  numFinalArgs,
600  perfuncstate->winCollation,
601  (void *) winstate, NULL);
602  fcinfo->args[0].value =
604  peraggstate->transValueIsNull,
605  peraggstate->transtypeLen);
606  fcinfo->args[0].isnull = peraggstate->transValueIsNull;
607  anynull = peraggstate->transValueIsNull;
608 
609  /* Fill any remaining argument positions with nulls */
610  for (i = 1; i < numFinalArgs; i++)
611  {
612  fcinfo->args[i].value = (Datum) 0;
613  fcinfo->args[i].isnull = true;
614  anynull = true;
615  }
616 
617  if (fcinfo->flinfo->fn_strict && anynull)
618  {
619  /* don't call a strict function with NULL inputs */
620  *result = (Datum) 0;
621  *isnull = true;
622  }
623  else
624  {
625  winstate->curaggcontext = peraggstate->aggcontext;
626  *result = FunctionCallInvoke(fcinfo);
627  winstate->curaggcontext = NULL;
628  *isnull = fcinfo->isnull;
629  }
630  }
631  else
632  {
633  /* Don't need MakeExpandedObjectReadOnly; datumCopy will copy it */
634  *result = peraggstate->transValue;
635  *isnull = peraggstate->transValueIsNull;
636  }
637 
638  /*
639  * If result is pass-by-ref, make sure it is in the right context.
640  */
641  if (!peraggstate->resulttypeByVal && !*isnull &&
643  DatumGetPointer(*result)))
644  *result = datumCopy(*result,
645  peraggstate->resulttypeByVal,
646  peraggstate->resulttypeLen);
647  MemoryContextSwitchTo(oldContext);
648 }
649 
650 /*
651  * eval_windowaggregates
652  * evaluate plain aggregates being used as window functions
653  *
654  * This differs from nodeAgg.c in two ways. First, if the window's frame
655  * start position moves, we use the inverse transition function (if it exists)
656  * to remove rows from the transition value. And second, we expect to be
657  * able to call aggregate final functions repeatedly after aggregating more
658  * data onto the same transition value. This is not a behavior required by
659  * nodeAgg.c.
660  */
661 static void
663 {
664  WindowStatePerAgg peraggstate;
665  int wfuncno,
666  numaggs,
667  numaggs_restart,
668  i;
669  int64 aggregatedupto_nonrestarted;
670  MemoryContext oldContext;
671  ExprContext *econtext;
672  WindowObject agg_winobj;
673  TupleTableSlot *agg_row_slot;
674  TupleTableSlot *temp_slot;
675 
676  numaggs = winstate->numaggs;
677  if (numaggs == 0)
678  return; /* nothing to do */
679 
680  /* final output execution is in ps_ExprContext */
681  econtext = winstate->ss.ps.ps_ExprContext;
682  agg_winobj = winstate->agg_winobj;
683  agg_row_slot = winstate->agg_row_slot;
684  temp_slot = winstate->temp_slot_1;
685 
686  /*
687  * If the window's frame start clause is UNBOUNDED_PRECEDING and no
688  * exclusion clause is specified, then the window frame consists of a
689  * contiguous group of rows extending forward from the start of the
690  * partition, and rows only enter the frame, never exit it, as the current
691  * row advances forward. This makes it possible to use an incremental
692  * strategy for evaluating aggregates: we run the transition function for
693  * each row added to the frame, and run the final function whenever we
694  * need the current aggregate value. This is considerably more efficient
695  * than the naive approach of re-running the entire aggregate calculation
696  * for each current row. It does assume that the final function doesn't
697  * damage the running transition value, but we have the same assumption in
698  * nodeAgg.c too (when it rescans an existing hash table).
699  *
700  * If the frame start does sometimes move, we can still optimize as above
701  * whenever successive rows share the same frame head, but if the frame
702  * head moves beyond the previous head we try to remove those rows using
703  * the aggregate's inverse transition function. This function restores
704  * the aggregate's current state to what it would be if the removed row
705  * had never been aggregated in the first place. Inverse transition
706  * functions may optionally return NULL, indicating that the function was
707  * unable to remove the tuple from aggregation. If this happens, or if
708  * the aggregate doesn't have an inverse transition function at all, we
709  * must perform the aggregation all over again for all tuples within the
710  * new frame boundaries.
711  *
712  * If there's any exclusion clause, then we may have to aggregate over a
713  * non-contiguous set of rows, so we punt and recalculate for every row.
714  * (For some frame end choices, it might be that the frame is always
715  * contiguous anyway, but that's an optimization to investigate later.)
716  *
717  * In many common cases, multiple rows share the same frame and hence the
718  * same aggregate value. (In particular, if there's no ORDER BY in a RANGE
719  * window, then all rows are peers and so they all have window frame equal
720  * to the whole partition.) We optimize such cases by calculating the
721  * aggregate value once when we reach the first row of a peer group, and
722  * then returning the saved value for all subsequent rows.
723  *
724  * 'aggregatedupto' keeps track of the first row that has not yet been
725  * accumulated into the aggregate transition values. Whenever we start a
726  * new peer group, we accumulate forward to the end of the peer group.
727  */
728 
729  /*
730  * First, update the frame head position.
731  *
732  * The frame head should never move backwards, and the code below wouldn't
733  * cope if it did, so for safety we complain if it does.
734  */
735  update_frameheadpos(winstate);
736  if (winstate->frameheadpos < winstate->aggregatedbase)
737  elog(ERROR, "window frame head moved backward");
738 
739  /*
740  * If the frame didn't change compared to the previous row, we can re-use
741  * the result values that were previously saved at the bottom of this
742  * function. Since we don't know the current frame's end yet, this is not
743  * possible to check for fully. But if the frame end mode is UNBOUNDED
744  * FOLLOWING or CURRENT ROW, no exclusion clause is specified, and the
745  * current row lies within the previous row's frame, then the two frames'
746  * ends must coincide. Note that on the first row aggregatedbase ==
747  * aggregatedupto, meaning this test must fail, so we don't need to check
748  * the "there was no previous row" case explicitly here.
749  */
750  if (winstate->aggregatedbase == winstate->frameheadpos &&
753  !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
754  winstate->aggregatedbase <= winstate->currentpos &&
755  winstate->aggregatedupto > winstate->currentpos)
756  {
757  for (i = 0; i < numaggs; i++)
758  {
759  peraggstate = &winstate->peragg[i];
760  wfuncno = peraggstate->wfuncno;
761  econtext->ecxt_aggvalues[wfuncno] = peraggstate->resultValue;
762  econtext->ecxt_aggnulls[wfuncno] = peraggstate->resultValueIsNull;
763  }
764  return;
765  }
766 
767  /*----------
768  * Initialize restart flags.
769  *
770  * We restart the aggregation:
771  * - if we're processing the first row in the partition, or
772  * - if the frame's head moved and we cannot use an inverse
773  * transition function, or
774  * - we have an EXCLUSION clause, or
775  * - if the new frame doesn't overlap the old one
776  *
777  * Note that we don't strictly need to restart in the last case, but if
778  * we're going to remove all rows from the aggregation anyway, a restart
779  * surely is faster.
780  *----------
781  */
782  numaggs_restart = 0;
783  for (i = 0; i < numaggs; i++)
784  {
785  peraggstate = &winstate->peragg[i];
786  if (winstate->currentpos == 0 ||
787  (winstate->aggregatedbase != winstate->frameheadpos &&
788  !OidIsValid(peraggstate->invtransfn_oid)) ||
789  (winstate->frameOptions & FRAMEOPTION_EXCLUSION) ||
790  winstate->aggregatedupto <= winstate->frameheadpos)
791  {
792  peraggstate->restart = true;
793  numaggs_restart++;
794  }
795  else
796  peraggstate->restart = false;
797  }
798 
799  /*
800  * If we have any possibly-moving aggregates, attempt to advance
801  * aggregatedbase to match the frame's head by removing input rows that
802  * fell off the top of the frame from the aggregations. This can fail,
803  * i.e. advance_windowaggregate_base() can return false, in which case
804  * we'll restart that aggregate below.
805  */
806  while (numaggs_restart < numaggs &&
807  winstate->aggregatedbase < winstate->frameheadpos)
808  {
809  /*
810  * Fetch the next tuple of those being removed. This should never fail
811  * as we should have been here before.
812  */
813  if (!window_gettupleslot(agg_winobj, winstate->aggregatedbase,
814  temp_slot))
815  elog(ERROR, "could not re-fetch previously fetched frame row");
816 
817  /* Set tuple context for evaluation of aggregate arguments */
818  winstate->tmpcontext->ecxt_outertuple = temp_slot;
819 
820  /*
821  * Perform the inverse transition for each aggregate function in the
822  * window, unless it has already been marked as needing a restart.
823  */
824  for (i = 0; i < numaggs; i++)
825  {
826  bool ok;
827 
828  peraggstate = &winstate->peragg[i];
829  if (peraggstate->restart)
830  continue;
831 
832  wfuncno = peraggstate->wfuncno;
833  ok = advance_windowaggregate_base(winstate,
834  &winstate->perfunc[wfuncno],
835  peraggstate);
836  if (!ok)
837  {
838  /* Inverse transition function has failed, must restart */
839  peraggstate->restart = true;
840  numaggs_restart++;
841  }
842  }
843 
844  /* Reset per-input-tuple context after each tuple */
845  ResetExprContext(winstate->tmpcontext);
846 
847  /* And advance the aggregated-row state */
848  winstate->aggregatedbase++;
849  ExecClearTuple(temp_slot);
850  }
851 
852  /*
853  * If we successfully advanced the base rows of all the aggregates,
854  * aggregatedbase now equals frameheadpos; but if we failed for any, we
855  * must forcibly update aggregatedbase.
856  */
857  winstate->aggregatedbase = winstate->frameheadpos;
858 
859  /*
860  * If we created a mark pointer for aggregates, keep it pushed up to frame
861  * head, so that tuplestore can discard unnecessary rows.
862  */
863  if (agg_winobj->markptr >= 0)
864  WinSetMarkPosition(agg_winobj, winstate->frameheadpos);
865 
866  /*
867  * Now restart the aggregates that require it.
868  *
869  * We assume that aggregates using the shared context always restart if
870  * *any* aggregate restarts, and we may thus clean up the shared
871  * aggcontext if that is the case. Private aggcontexts are reset by
872  * initialize_windowaggregate() if their owning aggregate restarts. If we
873  * aren't restarting an aggregate, we need to free any previously saved
874  * result for it, else we'll leak memory.
875  */
876  if (numaggs_restart > 0)
878  for (i = 0; i < numaggs; i++)
879  {
880  peraggstate = &winstate->peragg[i];
881 
882  /* Aggregates using the shared ctx must restart if *any* agg does */
883  Assert(peraggstate->aggcontext != winstate->aggcontext ||
884  numaggs_restart == 0 ||
885  peraggstate->restart);
886 
887  if (peraggstate->restart)
888  {
889  wfuncno = peraggstate->wfuncno;
891  &winstate->perfunc[wfuncno],
892  peraggstate);
893  }
894  else if (!peraggstate->resultValueIsNull)
895  {
896  if (!peraggstate->resulttypeByVal)
897  pfree(DatumGetPointer(peraggstate->resultValue));
898  peraggstate->resultValue = (Datum) 0;
899  peraggstate->resultValueIsNull = true;
900  }
901  }
902 
903  /*
904  * Non-restarted aggregates now contain the rows between aggregatedbase
905  * (i.e., frameheadpos) and aggregatedupto, while restarted aggregates
906  * contain no rows. If there are any restarted aggregates, we must thus
907  * begin aggregating anew at frameheadpos, otherwise we may simply
908  * continue at aggregatedupto. We must remember the old value of
909  * aggregatedupto to know how long to skip advancing non-restarted
910  * aggregates. If we modify aggregatedupto, we must also clear
911  * agg_row_slot, per the loop invariant below.
912  */
913  aggregatedupto_nonrestarted = winstate->aggregatedupto;
914  if (numaggs_restart > 0 &&
915  winstate->aggregatedupto != winstate->frameheadpos)
916  {
917  winstate->aggregatedupto = winstate->frameheadpos;
918  ExecClearTuple(agg_row_slot);
919  }
920 
921  /*
922  * Advance until we reach a row not in frame (or end of partition).
923  *
924  * Note the loop invariant: agg_row_slot is either empty or holds the row
925  * at position aggregatedupto. We advance aggregatedupto after processing
926  * a row.
927  */
928  for (;;)
929  {
930  int ret;
931 
932  /* Fetch next row if we didn't already */
933  if (TupIsNull(agg_row_slot))
934  {
935  if (!window_gettupleslot(agg_winobj, winstate->aggregatedupto,
936  agg_row_slot))
937  break; /* must be end of partition */
938  }
939 
940  /*
941  * Exit loop if no more rows can be in frame. Skip aggregation if
942  * current row is not in frame but there might be more in the frame.
943  */
944  ret = row_is_in_frame(winstate, winstate->aggregatedupto, agg_row_slot);
945  if (ret < 0)
946  break;
947  if (ret == 0)
948  goto next_tuple;
949 
950  /* Set tuple context for evaluation of aggregate arguments */
951  winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
952 
953  /* Accumulate row into the aggregates */
954  for (i = 0; i < numaggs; i++)
955  {
956  peraggstate = &winstate->peragg[i];
957 
958  /* Non-restarted aggs skip until aggregatedupto_nonrestarted */
959  if (!peraggstate->restart &&
960  winstate->aggregatedupto < aggregatedupto_nonrestarted)
961  continue;
962 
963  wfuncno = peraggstate->wfuncno;
964  advance_windowaggregate(winstate,
965  &winstate->perfunc[wfuncno],
966  peraggstate);
967  }
968 
969 next_tuple:
970  /* Reset per-input-tuple context after each tuple */
971  ResetExprContext(winstate->tmpcontext);
972 
973  /* And advance the aggregated-row state */
974  winstate->aggregatedupto++;
975  ExecClearTuple(agg_row_slot);
976  }
977 
978  /* The frame's end is not supposed to move backwards, ever */
979  Assert(aggregatedupto_nonrestarted <= winstate->aggregatedupto);
980 
981  /*
982  * finalize aggregates and fill result/isnull fields.
983  */
984  for (i = 0; i < numaggs; i++)
985  {
986  Datum *result;
987  bool *isnull;
988 
989  peraggstate = &winstate->peragg[i];
990  wfuncno = peraggstate->wfuncno;
991  result = &econtext->ecxt_aggvalues[wfuncno];
992  isnull = &econtext->ecxt_aggnulls[wfuncno];
993  finalize_windowaggregate(winstate,
994  &winstate->perfunc[wfuncno],
995  peraggstate,
996  result, isnull);
997 
998  /*
999  * save the result in case next row shares the same frame.
1000  *
1001  * XXX in some framing modes, eg ROWS/END_CURRENT_ROW, we can know in
1002  * advance that the next row can't possibly share the same frame. Is
1003  * it worth detecting that and skipping this code?
1004  */
1005  if (!peraggstate->resulttypeByVal && !*isnull)
1006  {
1007  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
1008  peraggstate->resultValue =
1009  datumCopy(*result,
1010  peraggstate->resulttypeByVal,
1011  peraggstate->resulttypeLen);
1012  MemoryContextSwitchTo(oldContext);
1013  }
1014  else
1015  {
1016  peraggstate->resultValue = *result;
1017  }
1018  peraggstate->resultValueIsNull = *isnull;
1019  }
1020 }
1021 
1022 /*
1023  * eval_windowfunction
1024  *
1025  * Arguments of window functions are not evaluated here, because a window
1026  * function can need random access to arbitrary rows in the partition.
1027  * The window function uses the special WinGetFuncArgInPartition and
1028  * WinGetFuncArgInFrame functions to evaluate the arguments for the rows
1029  * it wants.
1030  */
1031 static void
1033  Datum *result, bool *isnull)
1034 {
1035  LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1036  MemoryContext oldContext;
1037 
1039 
1040  /*
1041  * We don't pass any normal arguments to a window function, but we do pass
1042  * it the number of arguments, in order to permit window function
1043  * implementations to support varying numbers of arguments. The real info
1044  * goes through the WindowObject, which is passed via fcinfo->context.
1045  */
1046  InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1047  perfuncstate->numArguments,
1048  perfuncstate->winCollation,
1049  (void *) perfuncstate->winobj, NULL);
1050  /* Just in case, make all the regular argument slots be null */
1051  for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1052  fcinfo->args[argno].isnull = true;
1053  /* Window functions don't have a current aggregate context, either */
1054  winstate->curaggcontext = NULL;
1055 
1056  *result = FunctionCallInvoke(fcinfo);
1057  *isnull = fcinfo->isnull;
1058 
1059  /*
1060  * Make sure pass-by-ref data is allocated in the appropriate context. (We
1061  * need this in case the function returns a pointer into some short-lived
1062  * tuple, as is entirely possible.)
1063  */
1064  if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1066  DatumGetPointer(*result)))
1067  *result = datumCopy(*result,
1068  perfuncstate->resulttypeByVal,
1069  perfuncstate->resulttypeLen);
1070 
1071  MemoryContextSwitchTo(oldContext);
1072 }
1073 
1074 /*
1075  * begin_partition
1076  * Start buffering rows of the next partition.
1077  */
1078 static void
1080 {
1081  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1082  PlanState *outerPlan = outerPlanState(winstate);
1083  int frameOptions = winstate->frameOptions;
1084  int numfuncs = winstate->numfuncs;
1085  int i;
1086 
1087  winstate->partition_spooled = false;
1088  winstate->framehead_valid = false;
1089  winstate->frametail_valid = false;
1090  winstate->grouptail_valid = false;
1091  winstate->spooled_rows = 0;
1092  winstate->currentpos = 0;
1093  winstate->frameheadpos = 0;
1094  winstate->frametailpos = 0;
1095  winstate->currentgroup = 0;
1096  winstate->frameheadgroup = 0;
1097  winstate->frametailgroup = 0;
1098  winstate->groupheadpos = 0;
1099  winstate->grouptailpos = -1; /* see update_grouptailpos */
1100  ExecClearTuple(winstate->agg_row_slot);
1101  if (winstate->framehead_slot)
1102  ExecClearTuple(winstate->framehead_slot);
1103  if (winstate->frametail_slot)
1104  ExecClearTuple(winstate->frametail_slot);
1105 
1106  /*
1107  * If this is the very first partition, we need to fetch the first input
1108  * row to store in first_part_slot.
1109  */
1110  if (TupIsNull(winstate->first_part_slot))
1111  {
1112  TupleTableSlot *outerslot = ExecProcNode(outerPlan);
1113 
1114  if (!TupIsNull(outerslot))
1115  ExecCopySlot(winstate->first_part_slot, outerslot);
1116  else
1117  {
1118  /* outer plan is empty, so we have nothing to do */
1119  winstate->partition_spooled = true;
1120  winstate->more_partitions = false;
1121  return;
1122  }
1123  }
1124 
1125  /* Create new tuplestore for this partition */
1126  winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1127 
1128  /*
1129  * Set up read pointers for the tuplestore. The current pointer doesn't
1130  * need BACKWARD capability, but the per-window-function read pointers do,
1131  * and the aggregate pointer does if we might need to restart aggregation.
1132  */
1133  winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1134 
1135  /* reset default REWIND capability bit for current ptr */
1136  tuplestore_set_eflags(winstate->buffer, 0);
1137 
1138  /* create read pointers for aggregates, if needed */
1139  if (winstate->numaggs > 0)
1140  {
1141  WindowObject agg_winobj = winstate->agg_winobj;
1142  int readptr_flags = 0;
1143 
1144  /*
1145  * If the frame head is potentially movable, or we have an EXCLUSION
1146  * clause, we might need to restart aggregation ...
1147  */
1148  if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1149  (frameOptions & FRAMEOPTION_EXCLUSION))
1150  {
1151  /* ... so create a mark pointer to track the frame head */
1152  agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1153  /* and the read pointer will need BACKWARD capability */
1154  readptr_flags |= EXEC_FLAG_BACKWARD;
1155  }
1156 
1157  agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1158  readptr_flags);
1159  agg_winobj->markpos = -1;
1160  agg_winobj->seekpos = -1;
1161 
1162  /* Also reset the row counters for aggregates */
1163  winstate->aggregatedbase = 0;
1164  winstate->aggregatedupto = 0;
1165  }
1166 
1167  /* create mark and read pointers for each real window function */
1168  for (i = 0; i < numfuncs; i++)
1169  {
1170  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1171 
1172  if (!perfuncstate->plain_agg)
1173  {
1174  WindowObject winobj = perfuncstate->winobj;
1175 
1176  winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1177  0);
1178  winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1180  winobj->markpos = -1;
1181  winobj->seekpos = -1;
1182  }
1183  }
1184 
1185  /*
1186  * If we are in RANGE or GROUPS mode, then determining frame boundaries
1187  * requires physical access to the frame endpoint rows, except in certain
1188  * degenerate cases. We create read pointers to point to those rows, to
1189  * simplify access and ensure that the tuplestore doesn't discard the
1190  * endpoint rows prematurely. (Must create pointers in exactly the same
1191  * cases that update_frameheadpos and update_frametailpos need them.)
1192  */
1193  winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1194 
1195  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1196  {
1197  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1198  node->ordNumCols != 0) ||
1199  (frameOptions & FRAMEOPTION_START_OFFSET))
1200  winstate->framehead_ptr =
1201  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1202  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1203  node->ordNumCols != 0) ||
1204  (frameOptions & FRAMEOPTION_END_OFFSET))
1205  winstate->frametail_ptr =
1206  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1207  }
1208 
1209  /*
1210  * If we have an exclusion clause that requires knowing the boundaries of
1211  * the current row's peer group, we create a read pointer to track the
1212  * tail position of the peer group (i.e., first row of the next peer
1213  * group). The head position does not require its own pointer because we
1214  * maintain that as a side effect of advancing the current row.
1215  */
1216  winstate->grouptail_ptr = -1;
1217 
1218  if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1220  node->ordNumCols != 0)
1221  {
1222  winstate->grouptail_ptr =
1223  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1224  }
1225 
1226  /*
1227  * Store the first tuple into the tuplestore (it's always available now;
1228  * we either read it above, or saved it at the end of previous partition)
1229  */
1230  tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1231  winstate->spooled_rows++;
1232 }
1233 
1234 /*
1235  * Read tuples from the outer node, up to and including position 'pos', and
1236  * store them into the tuplestore. If pos is -1, reads the whole partition.
1237  */
1238 static void
1239 spool_tuples(WindowAggState *winstate, int64 pos)
1240 {
1241  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1243  TupleTableSlot *outerslot;
1244  MemoryContext oldcontext;
1245 
1246  if (!winstate->buffer)
1247  return; /* just a safety check */
1248  if (winstate->partition_spooled)
1249  return; /* whole partition done already */
1250 
1251  /*
1252  * When in pass-through mode we can just exhaust all tuples in the current
1253  * partition. We don't need these tuples for any further window function
1254  * evaluation, however, we do need to keep them around if we're not the
1255  * top-level window as another WindowAgg node above must see these.
1256  */
1257  if (winstate->status != WINDOWAGG_RUN)
1258  {
1259  Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1260  winstate->status == WINDOWAGG_PASSTHROUGH_STRICT);
1261 
1262  pos = -1;
1263  }
1264 
1265  /*
1266  * If the tuplestore has spilled to disk, alternate reading and writing
1267  * becomes quite expensive due to frequent buffer flushes. It's cheaper
1268  * to force the entire partition to get spooled in one go.
1269  *
1270  * XXX this is a horrid kluge --- it'd be better to fix the performance
1271  * problem inside tuplestore. FIXME
1272  */
1273  else if (!tuplestore_in_memory(winstate->buffer))
1274  pos = -1;
1275 
1276  outerPlan = outerPlanState(winstate);
1277 
1278  /* Must be in query context to call outerplan */
1280 
1281  while (winstate->spooled_rows <= pos || pos == -1)
1282  {
1283  outerslot = ExecProcNode(outerPlan);
1284  if (TupIsNull(outerslot))
1285  {
1286  /* reached the end of the last partition */
1287  winstate->partition_spooled = true;
1288  winstate->more_partitions = false;
1289  break;
1290  }
1291 
1292  if (node->partNumCols > 0)
1293  {
1294  ExprContext *econtext = winstate->tmpcontext;
1295 
1296  econtext->ecxt_innertuple = winstate->first_part_slot;
1297  econtext->ecxt_outertuple = outerslot;
1298 
1299  /* Check if this tuple still belongs to the current partition */
1300  if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1301  {
1302  /*
1303  * end of partition; copy the tuple for the next cycle.
1304  */
1305  ExecCopySlot(winstate->first_part_slot, outerslot);
1306  winstate->partition_spooled = true;
1307  winstate->more_partitions = true;
1308  break;
1309  }
1310  }
1311 
1312  /*
1313  * Remember the tuple unless we're the top-level window and we're in
1314  * pass-through mode.
1315  */
1316  if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1317  {
1318  /* Still in partition, so save it into the tuplestore */
1319  tuplestore_puttupleslot(winstate->buffer, outerslot);
1320  winstate->spooled_rows++;
1321  }
1322  }
1323 
1324  MemoryContextSwitchTo(oldcontext);
1325 }
1326 
1327 /*
1328  * release_partition
1329  * clear information kept within a partition, including
1330  * tuplestore and aggregate results.
1331  */
1332 static void
1334 {
1335  int i;
1336 
1337  for (i = 0; i < winstate->numfuncs; i++)
1338  {
1339  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1340 
1341  /* Release any partition-local state of this window function */
1342  if (perfuncstate->winobj)
1343  perfuncstate->winobj->localmem = NULL;
1344  }
1345 
1346  /*
1347  * Release all partition-local memory (in particular, any partition-local
1348  * state that we might have trashed our pointers to in the above loop, and
1349  * any aggregate temp data). We don't rely on retail pfree because some
1350  * aggregates might have allocated data we don't have direct pointers to.
1351  */
1354  for (i = 0; i < winstate->numaggs; i++)
1355  {
1356  if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1358  }
1359 
1360  if (winstate->buffer)
1361  tuplestore_end(winstate->buffer);
1362  winstate->buffer = NULL;
1363  winstate->partition_spooled = false;
1364 }
1365 
1366 /*
1367  * row_is_in_frame
1368  * Determine whether a row is in the current row's window frame according
1369  * to our window framing rule
1370  *
1371  * The caller must have already determined that the row is in the partition
1372  * and fetched it into a slot. This function just encapsulates the framing
1373  * rules.
1374  *
1375  * Returns:
1376  * -1, if the row is out of frame and no succeeding rows can be in frame
1377  * 0, if the row is out of frame but succeeding rows might be in frame
1378  * 1, if the row is in frame
1379  *
1380  * May clobber winstate->temp_slot_2.
1381  */
1382 static int
1383 row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
1384 {
1385  int frameOptions = winstate->frameOptions;
1386 
1387  Assert(pos >= 0); /* else caller error */
1388 
1389  /*
1390  * First, check frame starting conditions. We might as well delegate this
1391  * to update_frameheadpos always; it doesn't add any notable cost.
1392  */
1393  update_frameheadpos(winstate);
1394  if (pos < winstate->frameheadpos)
1395  return 0;
1396 
1397  /*
1398  * Okay so far, now check frame ending conditions. Here, we avoid calling
1399  * update_frametailpos in simple cases, so as not to spool tuples further
1400  * ahead than necessary.
1401  */
1402  if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1403  {
1404  if (frameOptions & FRAMEOPTION_ROWS)
1405  {
1406  /* rows after current row are out of frame */
1407  if (pos > winstate->currentpos)
1408  return -1;
1409  }
1410  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1411  {
1412  /* following row that is not peer is out of frame */
1413  if (pos > winstate->currentpos &&
1414  !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1415  return -1;
1416  }
1417  else
1418  Assert(false);
1419  }
1420  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1421  {
1422  if (frameOptions & FRAMEOPTION_ROWS)
1423  {
1424  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1425 
1426  /* rows after current row + offset are out of frame */
1427  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1428  offset = -offset;
1429 
1430  if (pos > winstate->currentpos + offset)
1431  return -1;
1432  }
1433  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1434  {
1435  /* hard cases, so delegate to update_frametailpos */
1436  update_frametailpos(winstate);
1437  if (pos >= winstate->frametailpos)
1438  return -1;
1439  }
1440  else
1441  Assert(false);
1442  }
1443 
1444  /* Check exclusion clause */
1445  if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1446  {
1447  if (pos == winstate->currentpos)
1448  return 0;
1449  }
1450  else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1451  ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1452  pos != winstate->currentpos))
1453  {
1454  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1455 
1456  /* If no ORDER BY, all rows are peers with each other */
1457  if (node->ordNumCols == 0)
1458  return 0;
1459  /* Otherwise, check the group boundaries */
1460  if (pos >= winstate->groupheadpos)
1461  {
1462  update_grouptailpos(winstate);
1463  if (pos < winstate->grouptailpos)
1464  return 0;
1465  }
1466  }
1467 
1468  /* If we get here, it's in frame */
1469  return 1;
1470 }
1471 
1472 /*
1473  * update_frameheadpos
1474  * make frameheadpos valid for the current row
1475  *
1476  * Note that frameheadpos is computed without regard for any window exclusion
1477  * clause; the current row and/or its peers are considered part of the frame
1478  * for this purpose even if they must be excluded later.
1479  *
1480  * May clobber winstate->temp_slot_2.
1481  */
1482 static void
1484 {
1485  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1486  int frameOptions = winstate->frameOptions;
1487  MemoryContext oldcontext;
1488 
1489  if (winstate->framehead_valid)
1490  return; /* already known for current row */
1491 
1492  /* We may be called in a short-lived context */
1494 
1495  if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1496  {
1497  /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1498  winstate->frameheadpos = 0;
1499  winstate->framehead_valid = true;
1500  }
1501  else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1502  {
1503  if (frameOptions & FRAMEOPTION_ROWS)
1504  {
1505  /* In ROWS mode, frame head is the same as current */
1506  winstate->frameheadpos = winstate->currentpos;
1507  winstate->framehead_valid = true;
1508  }
1509  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1510  {
1511  /* If no ORDER BY, all rows are peers with each other */
1512  if (node->ordNumCols == 0)
1513  {
1514  winstate->frameheadpos = 0;
1515  winstate->framehead_valid = true;
1516  MemoryContextSwitchTo(oldcontext);
1517  return;
1518  }
1519 
1520  /*
1521  * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1522  * first row that is a peer of current row. We keep a copy of the
1523  * last-known frame head row in framehead_slot, and advance as
1524  * necessary. Note that if we reach end of partition, we will
1525  * leave frameheadpos = end+1 and framehead_slot empty.
1526  */
1528  winstate->framehead_ptr);
1529  if (winstate->frameheadpos == 0 &&
1530  TupIsNull(winstate->framehead_slot))
1531  {
1532  /* fetch first row into framehead_slot, if we didn't already */
1533  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1534  winstate->framehead_slot))
1535  elog(ERROR, "unexpected end of tuplestore");
1536  }
1537 
1538  while (!TupIsNull(winstate->framehead_slot))
1539  {
1540  if (are_peers(winstate, winstate->framehead_slot,
1541  winstate->ss.ss_ScanTupleSlot))
1542  break; /* this row is the correct frame head */
1543  /* Note we advance frameheadpos even if the fetch fails */
1544  winstate->frameheadpos++;
1545  spool_tuples(winstate, winstate->frameheadpos);
1546  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1547  winstate->framehead_slot))
1548  break; /* end of partition */
1549  }
1550  winstate->framehead_valid = true;
1551  }
1552  else
1553  Assert(false);
1554  }
1555  else if (frameOptions & FRAMEOPTION_START_OFFSET)
1556  {
1557  if (frameOptions & FRAMEOPTION_ROWS)
1558  {
1559  /* In ROWS mode, bound is physically n before/after current */
1560  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1561 
1562  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1563  offset = -offset;
1564 
1565  winstate->frameheadpos = winstate->currentpos + offset;
1566  /* frame head can't go before first row */
1567  if (winstate->frameheadpos < 0)
1568  winstate->frameheadpos = 0;
1569  else if (winstate->frameheadpos > winstate->currentpos + 1)
1570  {
1571  /* make sure frameheadpos is not past end of partition */
1572  spool_tuples(winstate, winstate->frameheadpos - 1);
1573  if (winstate->frameheadpos > winstate->spooled_rows)
1574  winstate->frameheadpos = winstate->spooled_rows;
1575  }
1576  winstate->framehead_valid = true;
1577  }
1578  else if (frameOptions & FRAMEOPTION_RANGE)
1579  {
1580  /*
1581  * In RANGE START_OFFSET mode, frame head is the first row that
1582  * satisfies the in_range constraint relative to the current row.
1583  * We keep a copy of the last-known frame head row in
1584  * framehead_slot, and advance as necessary. Note that if we
1585  * reach end of partition, we will leave frameheadpos = end+1 and
1586  * framehead_slot empty.
1587  */
1588  int sortCol = node->ordColIdx[0];
1589  bool sub,
1590  less;
1591 
1592  /* We must have an ordering column */
1593  Assert(node->ordNumCols == 1);
1594 
1595  /* Precompute flags for in_range checks */
1596  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1597  sub = true; /* subtract startOffset from current row */
1598  else
1599  sub = false; /* add it */
1600  less = false; /* normally, we want frame head >= sum */
1601  /* If sort order is descending, flip both flags */
1602  if (!winstate->inRangeAsc)
1603  {
1604  sub = !sub;
1605  less = true;
1606  }
1607 
1609  winstate->framehead_ptr);
1610  if (winstate->frameheadpos == 0 &&
1611  TupIsNull(winstate->framehead_slot))
1612  {
1613  /* fetch first row into framehead_slot, if we didn't already */
1614  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1615  winstate->framehead_slot))
1616  elog(ERROR, "unexpected end of tuplestore");
1617  }
1618 
1619  while (!TupIsNull(winstate->framehead_slot))
1620  {
1621  Datum headval,
1622  currval;
1623  bool headisnull,
1624  currisnull;
1625 
1626  headval = slot_getattr(winstate->framehead_slot, sortCol,
1627  &headisnull);
1628  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1629  &currisnull);
1630  if (headisnull || currisnull)
1631  {
1632  /* order of the rows depends only on nulls_first */
1633  if (winstate->inRangeNullsFirst)
1634  {
1635  /* advance head if head is null and curr is not */
1636  if (!headisnull || currisnull)
1637  break;
1638  }
1639  else
1640  {
1641  /* advance head if head is not null and curr is null */
1642  if (headisnull || !currisnull)
1643  break;
1644  }
1645  }
1646  else
1647  {
1649  winstate->inRangeColl,
1650  headval,
1651  currval,
1652  winstate->startOffsetValue,
1653  BoolGetDatum(sub),
1654  BoolGetDatum(less))))
1655  break; /* this row is the correct frame head */
1656  }
1657  /* Note we advance frameheadpos even if the fetch fails */
1658  winstate->frameheadpos++;
1659  spool_tuples(winstate, winstate->frameheadpos);
1660  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1661  winstate->framehead_slot))
1662  break; /* end of partition */
1663  }
1664  winstate->framehead_valid = true;
1665  }
1666  else if (frameOptions & FRAMEOPTION_GROUPS)
1667  {
1668  /*
1669  * In GROUPS START_OFFSET mode, frame head is the first row of the
1670  * first peer group whose number satisfies the offset constraint.
1671  * We keep a copy of the last-known frame head row in
1672  * framehead_slot, and advance as necessary. Note that if we
1673  * reach end of partition, we will leave frameheadpos = end+1 and
1674  * framehead_slot empty.
1675  */
1676  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1677  int64 minheadgroup;
1678 
1679  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1680  minheadgroup = winstate->currentgroup - offset;
1681  else
1682  minheadgroup = winstate->currentgroup + offset;
1683 
1685  winstate->framehead_ptr);
1686  if (winstate->frameheadpos == 0 &&
1687  TupIsNull(winstate->framehead_slot))
1688  {
1689  /* fetch first row into framehead_slot, if we didn't already */
1690  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1691  winstate->framehead_slot))
1692  elog(ERROR, "unexpected end of tuplestore");
1693  }
1694 
1695  while (!TupIsNull(winstate->framehead_slot))
1696  {
1697  if (winstate->frameheadgroup >= minheadgroup)
1698  break; /* this row is the correct frame head */
1699  ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1700  /* Note we advance frameheadpos even if the fetch fails */
1701  winstate->frameheadpos++;
1702  spool_tuples(winstate, winstate->frameheadpos);
1703  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1704  winstate->framehead_slot))
1705  break; /* end of partition */
1706  if (!are_peers(winstate, winstate->temp_slot_2,
1707  winstate->framehead_slot))
1708  winstate->frameheadgroup++;
1709  }
1710  ExecClearTuple(winstate->temp_slot_2);
1711  winstate->framehead_valid = true;
1712  }
1713  else
1714  Assert(false);
1715  }
1716  else
1717  Assert(false);
1718 
1719  MemoryContextSwitchTo(oldcontext);
1720 }
1721 
1722 /*
1723  * update_frametailpos
1724  * make frametailpos valid for the current row
1725  *
1726  * Note that frametailpos is computed without regard for any window exclusion
1727  * clause; the current row and/or its peers are considered part of the frame
1728  * for this purpose even if they must be excluded later.
1729  *
1730  * May clobber winstate->temp_slot_2.
1731  */
1732 static void
1734 {
1735  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1736  int frameOptions = winstate->frameOptions;
1737  MemoryContext oldcontext;
1738 
1739  if (winstate->frametail_valid)
1740  return; /* already known for current row */
1741 
1742  /* We may be called in a short-lived context */
1744 
1745  if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1746  {
1747  /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1748  spool_tuples(winstate, -1);
1749  winstate->frametailpos = winstate->spooled_rows;
1750  winstate->frametail_valid = true;
1751  }
1752  else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1753  {
1754  if (frameOptions & FRAMEOPTION_ROWS)
1755  {
1756  /* In ROWS mode, exactly the rows up to current are in frame */
1757  winstate->frametailpos = winstate->currentpos + 1;
1758  winstate->frametail_valid = true;
1759  }
1760  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1761  {
1762  /* If no ORDER BY, all rows are peers with each other */
1763  if (node->ordNumCols == 0)
1764  {
1765  spool_tuples(winstate, -1);
1766  winstate->frametailpos = winstate->spooled_rows;
1767  winstate->frametail_valid = true;
1768  MemoryContextSwitchTo(oldcontext);
1769  return;
1770  }
1771 
1772  /*
1773  * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1774  * row that is a peer of current row, frame tail is the row after
1775  * that (if any). We keep a copy of the last-known frame tail row
1776  * in frametail_slot, and advance as necessary. Note that if we
1777  * reach end of partition, we will leave frametailpos = end+1 and
1778  * frametail_slot empty.
1779  */
1781  winstate->frametail_ptr);
1782  if (winstate->frametailpos == 0 &&
1783  TupIsNull(winstate->frametail_slot))
1784  {
1785  /* fetch first row into frametail_slot, if we didn't already */
1786  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1787  winstate->frametail_slot))
1788  elog(ERROR, "unexpected end of tuplestore");
1789  }
1790 
1791  while (!TupIsNull(winstate->frametail_slot))
1792  {
1793  if (winstate->frametailpos > winstate->currentpos &&
1794  !are_peers(winstate, winstate->frametail_slot,
1795  winstate->ss.ss_ScanTupleSlot))
1796  break; /* this row is the frame tail */
1797  /* Note we advance frametailpos even if the fetch fails */
1798  winstate->frametailpos++;
1799  spool_tuples(winstate, winstate->frametailpos);
1800  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1801  winstate->frametail_slot))
1802  break; /* end of partition */
1803  }
1804  winstate->frametail_valid = true;
1805  }
1806  else
1807  Assert(false);
1808  }
1809  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1810  {
1811  if (frameOptions & FRAMEOPTION_ROWS)
1812  {
1813  /* In ROWS mode, bound is physically n before/after current */
1814  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1815 
1816  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1817  offset = -offset;
1818 
1819  winstate->frametailpos = winstate->currentpos + offset + 1;
1820  /* smallest allowable value of frametailpos is 0 */
1821  if (winstate->frametailpos < 0)
1822  winstate->frametailpos = 0;
1823  else if (winstate->frametailpos > winstate->currentpos + 1)
1824  {
1825  /* make sure frametailpos is not past end of partition */
1826  spool_tuples(winstate, winstate->frametailpos - 1);
1827  if (winstate->frametailpos > winstate->spooled_rows)
1828  winstate->frametailpos = winstate->spooled_rows;
1829  }
1830  winstate->frametail_valid = true;
1831  }
1832  else if (frameOptions & FRAMEOPTION_RANGE)
1833  {
1834  /*
1835  * In RANGE END_OFFSET mode, frame end is the last row that
1836  * satisfies the in_range constraint relative to the current row,
1837  * frame tail is the row after that (if any). We keep a copy of
1838  * the last-known frame tail row in frametail_slot, and advance as
1839  * necessary. Note that if we reach end of partition, we will
1840  * leave frametailpos = end+1 and frametail_slot empty.
1841  */
1842  int sortCol = node->ordColIdx[0];
1843  bool sub,
1844  less;
1845 
1846  /* We must have an ordering column */
1847  Assert(node->ordNumCols == 1);
1848 
1849  /* Precompute flags for in_range checks */
1850  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1851  sub = true; /* subtract endOffset from current row */
1852  else
1853  sub = false; /* add it */
1854  less = true; /* normally, we want frame tail <= sum */
1855  /* If sort order is descending, flip both flags */
1856  if (!winstate->inRangeAsc)
1857  {
1858  sub = !sub;
1859  less = false;
1860  }
1861 
1863  winstate->frametail_ptr);
1864  if (winstate->frametailpos == 0 &&
1865  TupIsNull(winstate->frametail_slot))
1866  {
1867  /* fetch first row into frametail_slot, if we didn't already */
1868  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1869  winstate->frametail_slot))
1870  elog(ERROR, "unexpected end of tuplestore");
1871  }
1872 
1873  while (!TupIsNull(winstate->frametail_slot))
1874  {
1875  Datum tailval,
1876  currval;
1877  bool tailisnull,
1878  currisnull;
1879 
1880  tailval = slot_getattr(winstate->frametail_slot, sortCol,
1881  &tailisnull);
1882  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1883  &currisnull);
1884  if (tailisnull || currisnull)
1885  {
1886  /* order of the rows depends only on nulls_first */
1887  if (winstate->inRangeNullsFirst)
1888  {
1889  /* advance tail if tail is null or curr is not */
1890  if (!tailisnull)
1891  break;
1892  }
1893  else
1894  {
1895  /* advance tail if tail is not null or curr is null */
1896  if (!currisnull)
1897  break;
1898  }
1899  }
1900  else
1901  {
1903  winstate->inRangeColl,
1904  tailval,
1905  currval,
1906  winstate->endOffsetValue,
1907  BoolGetDatum(sub),
1908  BoolGetDatum(less))))
1909  break; /* this row is the correct frame tail */
1910  }
1911  /* Note we advance frametailpos even if the fetch fails */
1912  winstate->frametailpos++;
1913  spool_tuples(winstate, winstate->frametailpos);
1914  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1915  winstate->frametail_slot))
1916  break; /* end of partition */
1917  }
1918  winstate->frametail_valid = true;
1919  }
1920  else if (frameOptions & FRAMEOPTION_GROUPS)
1921  {
1922  /*
1923  * In GROUPS END_OFFSET mode, frame end is the last row of the
1924  * last peer group whose number satisfies the offset constraint,
1925  * and frame tail is the row after that (if any). We keep a copy
1926  * of the last-known frame tail row in frametail_slot, and advance
1927  * as necessary. Note that if we reach end of partition, we will
1928  * leave frametailpos = end+1 and frametail_slot empty.
1929  */
1930  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1931  int64 maxtailgroup;
1932 
1933  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1934  maxtailgroup = winstate->currentgroup - offset;
1935  else
1936  maxtailgroup = winstate->currentgroup + offset;
1937 
1939  winstate->frametail_ptr);
1940  if (winstate->frametailpos == 0 &&
1941  TupIsNull(winstate->frametail_slot))
1942  {
1943  /* fetch first row into frametail_slot, if we didn't already */
1944  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1945  winstate->frametail_slot))
1946  elog(ERROR, "unexpected end of tuplestore");
1947  }
1948 
1949  while (!TupIsNull(winstate->frametail_slot))
1950  {
1951  if (winstate->frametailgroup > maxtailgroup)
1952  break; /* this row is the correct frame tail */
1953  ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
1954  /* Note we advance frametailpos even if the fetch fails */
1955  winstate->frametailpos++;
1956  spool_tuples(winstate, winstate->frametailpos);
1957  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1958  winstate->frametail_slot))
1959  break; /* end of partition */
1960  if (!are_peers(winstate, winstate->temp_slot_2,
1961  winstate->frametail_slot))
1962  winstate->frametailgroup++;
1963  }
1964  ExecClearTuple(winstate->temp_slot_2);
1965  winstate->frametail_valid = true;
1966  }
1967  else
1968  Assert(false);
1969  }
1970  else
1971  Assert(false);
1972 
1973  MemoryContextSwitchTo(oldcontext);
1974 }
1975 
1976 /*
1977  * update_grouptailpos
1978  * make grouptailpos valid for the current row
1979  *
1980  * May clobber winstate->temp_slot_2.
1981  */
1982 static void
1984 {
1985  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1986  MemoryContext oldcontext;
1987 
1988  if (winstate->grouptail_valid)
1989  return; /* already known for current row */
1990 
1991  /* We may be called in a short-lived context */
1993 
1994  /* If no ORDER BY, all rows are peers with each other */
1995  if (node->ordNumCols == 0)
1996  {
1997  spool_tuples(winstate, -1);
1998  winstate->grouptailpos = winstate->spooled_rows;
1999  winstate->grouptail_valid = true;
2000  MemoryContextSwitchTo(oldcontext);
2001  return;
2002  }
2003 
2004  /*
2005  * Because grouptail_valid is reset only when current row advances into a
2006  * new peer group, we always reach here knowing that grouptailpos needs to
2007  * be advanced by at least one row. Hence, unlike the otherwise similar
2008  * case for frame tail tracking, we do not need persistent storage of the
2009  * group tail row.
2010  */
2011  Assert(winstate->grouptailpos <= winstate->currentpos);
2013  winstate->grouptail_ptr);
2014  for (;;)
2015  {
2016  /* Note we advance grouptailpos even if the fetch fails */
2017  winstate->grouptailpos++;
2018  spool_tuples(winstate, winstate->grouptailpos);
2019  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2020  winstate->temp_slot_2))
2021  break; /* end of partition */
2022  if (winstate->grouptailpos > winstate->currentpos &&
2023  !are_peers(winstate, winstate->temp_slot_2,
2024  winstate->ss.ss_ScanTupleSlot))
2025  break; /* this row is the group tail */
2026  }
2027  ExecClearTuple(winstate->temp_slot_2);
2028  winstate->grouptail_valid = true;
2029 
2030  MemoryContextSwitchTo(oldcontext);
2031 }
2032 
2033 
2034 /* -----------------
2035  * ExecWindowAgg
2036  *
2037  * ExecWindowAgg receives tuples from its outer subplan and
2038  * stores them into a tuplestore, then processes window functions.
2039  * This node doesn't reduce nor qualify any row so the number of
2040  * returned rows is exactly the same as its outer subplan's result.
2041  * -----------------
2042  */
2043 static TupleTableSlot *
2045 {
2046  WindowAggState *winstate = castNode(WindowAggState, pstate);
2047  TupleTableSlot *slot;
2048  ExprContext *econtext;
2049  int i;
2050  int numfuncs;
2051 
2053 
2054  if (winstate->status == WINDOWAGG_DONE)
2055  return NULL;
2056 
2057  /*
2058  * Compute frame offset values, if any, during first call (or after a
2059  * rescan). These are assumed to hold constant throughout the scan; if
2060  * user gives us a volatile expression, we'll only use its initial value.
2061  */
2062  if (winstate->all_first)
2063  {
2064  int frameOptions = winstate->frameOptions;
2065  ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
2066  Datum value;
2067  bool isnull;
2068  int16 len;
2069  bool byval;
2070 
2071  if (frameOptions & FRAMEOPTION_START_OFFSET)
2072  {
2073  Assert(winstate->startOffset != NULL);
2075  econtext,
2076  &isnull);
2077  if (isnull)
2078  ereport(ERROR,
2079  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2080  errmsg("frame starting offset must not be null")));
2081  /* copy value into query-lifespan context */
2082  get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
2083  &len, &byval);
2084  winstate->startOffsetValue = datumCopy(value, byval, len);
2085  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2086  {
2087  /* value is known to be int8 */
2088  int64 offset = DatumGetInt64(value);
2089 
2090  if (offset < 0)
2091  ereport(ERROR,
2092  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2093  errmsg("frame starting offset must not be negative")));
2094  }
2095  }
2096  if (frameOptions & FRAMEOPTION_END_OFFSET)
2097  {
2098  Assert(winstate->endOffset != NULL);
2100  econtext,
2101  &isnull);
2102  if (isnull)
2103  ereport(ERROR,
2104  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2105  errmsg("frame ending offset must not be null")));
2106  /* copy value into query-lifespan context */
2107  get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2108  &len, &byval);
2109  winstate->endOffsetValue = datumCopy(value, byval, len);
2110  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2111  {
2112  /* value is known to be int8 */
2113  int64 offset = DatumGetInt64(value);
2114 
2115  if (offset < 0)
2116  ereport(ERROR,
2117  (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2118  errmsg("frame ending offset must not be negative")));
2119  }
2120  }
2121  winstate->all_first = false;
2122  }
2123 
2124  /* We need to loop as the runCondition or qual may filter out tuples */
2125  for (;;)
2126  {
2127  if (winstate->buffer == NULL)
2128  {
2129  /* Initialize for first partition and set current row = 0 */
2130  begin_partition(winstate);
2131  /* If there are no input rows, we'll detect that and exit below */
2132  }
2133  else
2134  {
2135  /* Advance current row within partition */
2136  winstate->currentpos++;
2137  /* This might mean that the frame moves, too */
2138  winstate->framehead_valid = false;
2139  winstate->frametail_valid = false;
2140  /* we don't need to invalidate grouptail here; see below */
2141  }
2142 
2143  /*
2144  * Spool all tuples up to and including the current row, if we haven't
2145  * already
2146  */
2147  spool_tuples(winstate, winstate->currentpos);
2148 
2149  /* Move to the next partition if we reached the end of this partition */
2150  if (winstate->partition_spooled &&
2151  winstate->currentpos >= winstate->spooled_rows)
2152  {
2153  release_partition(winstate);
2154 
2155  if (winstate->more_partitions)
2156  {
2157  begin_partition(winstate);
2158  Assert(winstate->spooled_rows > 0);
2159 
2160  /* Come out of pass-through mode when changing partition */
2161  winstate->status = WINDOWAGG_RUN;
2162  }
2163  else
2164  {
2165  /* No further partitions? We're done */
2166  winstate->status = WINDOWAGG_DONE;
2167  return NULL;
2168  }
2169  }
2170 
2171  /* final output execution is in ps_ExprContext */
2172  econtext = winstate->ss.ps.ps_ExprContext;
2173 
2174  /* Clear the per-output-tuple context for current row */
2175  ResetExprContext(econtext);
2176 
2177  /*
2178  * Read the current row from the tuplestore, and save in
2179  * ScanTupleSlot. (We can't rely on the outerplan's output slot
2180  * because we may have to read beyond the current row. Also, we have
2181  * to actually copy the row out of the tuplestore, since window
2182  * function evaluation might cause the tuplestore to dump its state to
2183  * disk.)
2184  *
2185  * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2186  * we must also detect entering a new peer group and update associated
2187  * state when that happens. We use temp_slot_2 to temporarily hold
2188  * the previous row for this purpose.
2189  *
2190  * Current row must be in the tuplestore, since we spooled it above.
2191  */
2192  tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
2193  if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2196  winstate->currentpos > 0)
2197  {
2198  ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2199  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2200  winstate->ss.ss_ScanTupleSlot))
2201  elog(ERROR, "unexpected end of tuplestore");
2202  if (!are_peers(winstate, winstate->temp_slot_2,
2203  winstate->ss.ss_ScanTupleSlot))
2204  {
2205  winstate->currentgroup++;
2206  winstate->groupheadpos = winstate->currentpos;
2207  winstate->grouptail_valid = false;
2208  }
2209  ExecClearTuple(winstate->temp_slot_2);
2210  }
2211  else
2212  {
2213  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2214  winstate->ss.ss_ScanTupleSlot))
2215  elog(ERROR, "unexpected end of tuplestore");
2216  }
2217 
2218  /* don't evaluate the window functions when we're in pass-through mode */
2219  if (winstate->status == WINDOWAGG_RUN)
2220  {
2221  /*
2222  * Evaluate true window functions
2223  */
2224  numfuncs = winstate->numfuncs;
2225  for (i = 0; i < numfuncs; i++)
2226  {
2227  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2228 
2229  if (perfuncstate->plain_agg)
2230  continue;
2231  eval_windowfunction(winstate, perfuncstate,
2232  &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2233  &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2234  }
2235 
2236  /*
2237  * Evaluate aggregates
2238  */
2239  if (winstate->numaggs > 0)
2240  eval_windowaggregates(winstate);
2241  }
2242 
2243  /*
2244  * If we have created auxiliary read pointers for the frame or group
2245  * boundaries, force them to be kept up-to-date, because we don't know
2246  * whether the window function(s) will do anything that requires that.
2247  * Failing to advance the pointers would result in being unable to
2248  * trim data from the tuplestore, which is bad. (If we could know in
2249  * advance whether the window functions will use frame boundary info,
2250  * we could skip creating these pointers in the first place ... but
2251  * unfortunately the window function API doesn't require that.)
2252  */
2253  if (winstate->framehead_ptr >= 0)
2254  update_frameheadpos(winstate);
2255  if (winstate->frametail_ptr >= 0)
2256  update_frametailpos(winstate);
2257  if (winstate->grouptail_ptr >= 0)
2258  update_grouptailpos(winstate);
2259 
2260  /*
2261  * Truncate any no-longer-needed rows from the tuplestore.
2262  */
2263  tuplestore_trim(winstate->buffer);
2264 
2265  /*
2266  * Form and return a projection tuple using the windowfunc results and
2267  * the current row. Setting ecxt_outertuple arranges that any Vars
2268  * will be evaluated with respect to that row.
2269  */
2270  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2271 
2272  slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2273 
2274  if (winstate->status == WINDOWAGG_RUN)
2275  {
2276  econtext->ecxt_scantuple = slot;
2277 
2278  /*
2279  * Now evaluate the run condition to see if we need to go into
2280  * pass-through mode, or maybe stop completely.
2281  */
2282  if (!ExecQual(winstate->runcondition, econtext))
2283  {
2284  /*
2285  * Determine which mode to move into. If there is no
2286  * PARTITION BY clause and we're the top-level WindowAgg then
2287  * we're done. This tuple and any future tuples cannot
2288  * possibly match the runcondition. However, when there is a
2289  * PARTITION BY clause or we're not the top-level window we
2290  * can't just stop as we need to either process other
2291  * partitions or ensure WindowAgg nodes above us receive all
2292  * of the tuples they need to process their WindowFuncs.
2293  */
2294  if (winstate->use_pass_through)
2295  {
2296  /*
2297  * STRICT pass-through mode is required for the top window
2298  * when there is a PARTITION BY clause. Otherwise we must
2299  * ensure we store tuples that don't match the
2300  * runcondition so they're available to WindowAggs above.
2301  */
2302  if (winstate->top_window)
2303  {
2305  continue;
2306  }
2307  else
2308  winstate->status = WINDOWAGG_PASSTHROUGH;
2309  }
2310  else
2311  {
2312  /*
2313  * Pass-through not required. We can just return NULL.
2314  * Nothing else will match the runcondition.
2315  */
2316  winstate->status = WINDOWAGG_DONE;
2317  return NULL;
2318  }
2319  }
2320 
2321  /*
2322  * Filter out any tuples we don't need in the top-level WindowAgg.
2323  */
2324  if (!ExecQual(winstate->ss.ps.qual, econtext))
2325  {
2326  InstrCountFiltered1(winstate, 1);
2327  continue;
2328  }
2329 
2330  break;
2331  }
2332 
2333  /*
2334  * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2335  * we're anything apart from the top window.
2336  */
2337  else if (!winstate->top_window)
2338  break;
2339  }
2340 
2341  return slot;
2342 }
2343 
2344 /* -----------------
2345  * ExecInitWindowAgg
2346  *
2347  * Creates the run-time information for the WindowAgg node produced by the
2348  * planner and initializes its outer subtree
2349  * -----------------
2350  */
2352 ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
2353 {
2354  WindowAggState *winstate;
2355  Plan *outerPlan;
2356  ExprContext *econtext;
2357  ExprContext *tmpcontext;
2358  WindowStatePerFunc perfunc;
2359  WindowStatePerAgg peragg;
2360  int frameOptions = node->frameOptions;
2361  int numfuncs,
2362  wfuncno,
2363  numaggs,
2364  aggno;
2365  TupleDesc scanDesc;
2366  ListCell *l;
2367 
2368  /* check for unsupported flags */
2369  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2370 
2371  /*
2372  * create state structure
2373  */
2374  winstate = makeNode(WindowAggState);
2375  winstate->ss.ps.plan = (Plan *) node;
2376  winstate->ss.ps.state = estate;
2377  winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2378 
2379  /*
2380  * Create expression contexts. We need two, one for per-input-tuple
2381  * processing and one for per-output-tuple processing. We cheat a little
2382  * by using ExecAssignExprContext() to build both.
2383  */
2384  ExecAssignExprContext(estate, &winstate->ss.ps);
2385  tmpcontext = winstate->ss.ps.ps_ExprContext;
2386  winstate->tmpcontext = tmpcontext;
2387  ExecAssignExprContext(estate, &winstate->ss.ps);
2388 
2389  /* Create long-lived context for storage of partition-local memory etc */
2390  winstate->partcontext =
2392  "WindowAgg Partition",
2394 
2395  /*
2396  * Create mid-lived context for aggregate trans values etc.
2397  *
2398  * Note that moving aggregates each use their own private context, not
2399  * this one.
2400  */
2401  winstate->aggcontext =
2403  "WindowAgg Aggregates",
2405 
2406  /* Only the top-level WindowAgg may have a qual */
2407  Assert(node->plan.qual == NIL || node->topWindow);
2408 
2409  /* Initialize the qual */
2410  winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2411  (PlanState *) winstate);
2412 
2413  /*
2414  * Setup the run condition, if we received one from the query planner.
2415  * When set, this may allow us to move into pass-through mode so that we
2416  * don't have to perform any further evaluation of WindowFuncs in the
2417  * current partition or possibly stop returning tuples altogether when all
2418  * tuples are in the same partition.
2419  */
2420  winstate->runcondition = ExecInitQual(node->runCondition,
2421  (PlanState *) winstate);
2422 
2423  /*
2424  * When we're not the top-level WindowAgg node or we are but have a
2425  * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2426  * modes when the runCondition becomes false.
2427  */
2428  winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2429 
2430  /* remember if we're the top-window or we are below the top-window */
2431  winstate->top_window = node->topWindow;
2432 
2433  /*
2434  * initialize child nodes
2435  */
2436  outerPlan = outerPlan(node);
2437  outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2438 
2439  /*
2440  * initialize source tuple type (which is also the tuple type that we'll
2441  * store in the tuplestore and use in all our working slots).
2442  */
2444  scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2445 
2446  /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2447  winstate->ss.ps.outeropsset = true;
2448  winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2449  winstate->ss.ps.outeropsfixed = true;
2450 
2451  /*
2452  * tuple table initialization
2453  */
2454  winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2456  winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2458  winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2460  winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2462 
2463  /*
2464  * create frame head and tail slots only if needed (must create slots in
2465  * exactly the same cases that update_frameheadpos and update_frametailpos
2466  * need them)
2467  */
2468  winstate->framehead_slot = winstate->frametail_slot = NULL;
2469 
2470  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2471  {
2472  if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2473  node->ordNumCols != 0) ||
2474  (frameOptions & FRAMEOPTION_START_OFFSET))
2475  winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2477  if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2478  node->ordNumCols != 0) ||
2479  (frameOptions & FRAMEOPTION_END_OFFSET))
2480  winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2482  }
2483 
2484  /*
2485  * Initialize result slot, type and projection.
2486  */
2488  ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2489 
2490  /* Set up data for comparing tuples */
2491  if (node->partNumCols > 0)
2492  winstate->partEqfunction =
2493  execTuplesMatchPrepare(scanDesc,
2494  node->partNumCols,
2495  node->partColIdx,
2496  node->partOperators,
2497  node->partCollations,
2498  &winstate->ss.ps);
2499 
2500  if (node->ordNumCols > 0)
2501  winstate->ordEqfunction =
2502  execTuplesMatchPrepare(scanDesc,
2503  node->ordNumCols,
2504  node->ordColIdx,
2505  node->ordOperators,
2506  node->ordCollations,
2507  &winstate->ss.ps);
2508 
2509  /*
2510  * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2511  */
2512  numfuncs = winstate->numfuncs;
2513  numaggs = winstate->numaggs;
2514  econtext = winstate->ss.ps.ps_ExprContext;
2515  econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2516  econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2517 
2518  /*
2519  * allocate per-wfunc/per-agg state information.
2520  */
2521  perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2522  peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2523  winstate->perfunc = perfunc;
2524  winstate->peragg = peragg;
2525 
2526  wfuncno = -1;
2527  aggno = -1;
2528  foreach(l, winstate->funcs)
2529  {
2530  WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2531  WindowFunc *wfunc = wfuncstate->wfunc;
2532  WindowStatePerFunc perfuncstate;
2533  AclResult aclresult;
2534  int i;
2535 
2536  if (wfunc->winref != node->winref) /* planner screwed up? */
2537  elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2538  wfunc->winref, node->winref);
2539 
2540  /* Look for a previous duplicate window function */
2541  for (i = 0; i <= wfuncno; i++)
2542  {
2543  if (equal(wfunc, perfunc[i].wfunc) &&
2544  !contain_volatile_functions((Node *) wfunc))
2545  break;
2546  }
2547  if (i <= wfuncno)
2548  {
2549  /* Found a match to an existing entry, so just mark it */
2550  wfuncstate->wfuncno = i;
2551  continue;
2552  }
2553 
2554  /* Nope, so assign a new PerAgg record */
2555  perfuncstate = &perfunc[++wfuncno];
2556 
2557  /* Mark WindowFunc state node with assigned index in the result array */
2558  wfuncstate->wfuncno = wfuncno;
2559 
2560  /* Check permission to call window function */
2561  aclresult = pg_proc_aclcheck(wfunc->winfnoid, GetUserId(),
2562  ACL_EXECUTE);
2563  if (aclresult != ACLCHECK_OK)
2564  aclcheck_error(aclresult, OBJECT_FUNCTION,
2565  get_func_name(wfunc->winfnoid));
2567 
2568  /* Fill in the perfuncstate data */
2569  perfuncstate->wfuncstate = wfuncstate;
2570  perfuncstate->wfunc = wfunc;
2571  perfuncstate->numArguments = list_length(wfuncstate->args);
2572  perfuncstate->winCollation = wfunc->inputcollid;
2573 
2574  get_typlenbyval(wfunc->wintype,
2575  &perfuncstate->resulttypeLen,
2576  &perfuncstate->resulttypeByVal);
2577 
2578  /*
2579  * If it's really just a plain aggregate function, we'll emulate the
2580  * Agg environment for it.
2581  */
2582  perfuncstate->plain_agg = wfunc->winagg;
2583  if (wfunc->winagg)
2584  {
2585  WindowStatePerAgg peraggstate;
2586 
2587  perfuncstate->aggno = ++aggno;
2588  peraggstate = &winstate->peragg[aggno];
2589  initialize_peragg(winstate, wfunc, peraggstate);
2590  peraggstate->wfuncno = wfuncno;
2591  }
2592  else
2593  {
2595 
2596  winobj->winstate = winstate;
2597  winobj->argstates = wfuncstate->args;
2598  winobj->localmem = NULL;
2599  perfuncstate->winobj = winobj;
2600 
2601  /* It's a real window function, so set up to call it. */
2602  fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2603  econtext->ecxt_per_query_memory);
2604  fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2605  }
2606  }
2607 
2608  /* Update numfuncs, numaggs to match number of unique functions found */
2609  winstate->numfuncs = wfuncno + 1;
2610  winstate->numaggs = aggno + 1;
2611 
2612  /* Set up WindowObject for aggregates, if needed */
2613  if (winstate->numaggs > 0)
2614  {
2615  WindowObject agg_winobj = makeNode(WindowObjectData);
2616 
2617  agg_winobj->winstate = winstate;
2618  agg_winobj->argstates = NIL;
2619  agg_winobj->localmem = NULL;
2620  /* make sure markptr = -1 to invalidate. It may not get used */
2621  agg_winobj->markptr = -1;
2622  agg_winobj->readptr = -1;
2623  winstate->agg_winobj = agg_winobj;
2624  }
2625 
2626  /* Set the status to running */
2627  winstate->status = WINDOWAGG_RUN;
2628 
2629  /* copy frame options to state node for easy access */
2630  winstate->frameOptions = frameOptions;
2631 
2632  /* initialize frame bound offset expressions */
2633  winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2634  (PlanState *) winstate);
2635  winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2636  (PlanState *) winstate);
2637 
2638  /* Lookup in_range support functions if needed */
2639  if (OidIsValid(node->startInRangeFunc))
2640  fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2641  if (OidIsValid(node->endInRangeFunc))
2642  fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2643  winstate->inRangeColl = node->inRangeColl;
2644  winstate->inRangeAsc = node->inRangeAsc;
2645  winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2646 
2647  winstate->all_first = true;
2648  winstate->partition_spooled = false;
2649  winstate->more_partitions = false;
2650 
2651  return winstate;
2652 }
2653 
2654 /* -----------------
2655  * ExecEndWindowAgg
2656  * -----------------
2657  */
2658 void
2660 {
2662  int i;
2663 
2664  release_partition(node);
2665 
2669  ExecClearTuple(node->temp_slot_1);
2670  ExecClearTuple(node->temp_slot_2);
2671  if (node->framehead_slot)
2673  if (node->frametail_slot)
2675 
2676  /*
2677  * Free both the expr contexts.
2678  */
2679  ExecFreeExprContext(&node->ss.ps);
2680  node->ss.ps.ps_ExprContext = node->tmpcontext;
2681  ExecFreeExprContext(&node->ss.ps);
2682 
2683  for (i = 0; i < node->numaggs; i++)
2684  {
2685  if (node->peragg[i].aggcontext != node->aggcontext)
2687  }
2690 
2691  pfree(node->perfunc);
2692  pfree(node->peragg);
2693 
2694  outerPlan = outerPlanState(node);
2696 }
2697 
2698 /* -----------------
2699  * ExecReScanWindowAgg
2700  * -----------------
2701  */
2702 void
2704 {
2706  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2707 
2708  node->status = WINDOWAGG_RUN;
2709  node->all_first = true;
2710 
2711  /* release tuplestore et al */
2712  release_partition(node);
2713 
2714  /* release all temp tuples, but especially first_part_slot */
2718  ExecClearTuple(node->temp_slot_1);
2719  ExecClearTuple(node->temp_slot_2);
2720  if (node->framehead_slot)
2722  if (node->frametail_slot)
2724 
2725  /* Forget current wfunc values */
2726  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2727  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2728 
2729  /*
2730  * if chgParam of subnode is not null then plan will be re-scanned by
2731  * first ExecProcNode.
2732  */
2733  if (outerPlan->chgParam == NULL)
2735 }
2736 
2737 /*
2738  * initialize_peragg
2739  *
2740  * Almost same as in nodeAgg.c, except we don't support DISTINCT currently.
2741  */
2742 static WindowStatePerAggData *
2744  WindowStatePerAgg peraggstate)
2745 {
2746  Oid inputTypes[FUNC_MAX_ARGS];
2747  int numArguments;
2748  HeapTuple aggTuple;
2749  Form_pg_aggregate aggform;
2750  Oid aggtranstype;
2751  AttrNumber initvalAttNo;
2752  AclResult aclresult;
2753  bool use_ma_code;
2754  Oid transfn_oid,
2755  invtransfn_oid,
2756  finalfn_oid;
2757  bool finalextra;
2758  char finalmodify;
2759  Expr *transfnexpr,
2760  *invtransfnexpr,
2761  *finalfnexpr;
2762  Datum textInitVal;
2763  int i;
2764  ListCell *lc;
2765 
2766  numArguments = list_length(wfunc->args);
2767 
2768  i = 0;
2769  foreach(lc, wfunc->args)
2770  {
2771  inputTypes[i++] = exprType((Node *) lfirst(lc));
2772  }
2773 
2774  aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
2775  if (!HeapTupleIsValid(aggTuple))
2776  elog(ERROR, "cache lookup failed for aggregate %u",
2777  wfunc->winfnoid);
2778  aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
2779 
2780  /*
2781  * Figure out whether we want to use the moving-aggregate implementation,
2782  * and collect the right set of fields from the pg_attribute entry.
2783  *
2784  * It's possible that an aggregate would supply a safe moving-aggregate
2785  * implementation and an unsafe normal one, in which case our hand is
2786  * forced. Otherwise, if the frame head can't move, we don't need
2787  * moving-aggregate code. Even if we'd like to use it, don't do so if the
2788  * aggregate's arguments (and FILTER clause if any) contain any calls to
2789  * volatile functions. Otherwise, the difference between restarting and
2790  * not restarting the aggregation would be user-visible.
2791  */
2792  if (!OidIsValid(aggform->aggminvtransfn))
2793  use_ma_code = false; /* sine qua non */
2794  else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2795  aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2796  use_ma_code = true; /* decision forced by safety */
2798  use_ma_code = false; /* non-moving frame head */
2799  else if (contain_volatile_functions((Node *) wfunc))
2800  use_ma_code = false; /* avoid possible behavioral change */
2801  else
2802  use_ma_code = true; /* yes, let's use it */
2803  if (use_ma_code)
2804  {
2805  peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2806  peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2807  peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2808  finalextra = aggform->aggmfinalextra;
2809  finalmodify = aggform->aggmfinalmodify;
2810  aggtranstype = aggform->aggmtranstype;
2811  initvalAttNo = Anum_pg_aggregate_aggminitval;
2812  }
2813  else
2814  {
2815  peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2816  peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2817  peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2818  finalextra = aggform->aggfinalextra;
2819  finalmodify = aggform->aggfinalmodify;
2820  aggtranstype = aggform->aggtranstype;
2821  initvalAttNo = Anum_pg_aggregate_agginitval;
2822  }
2823 
2824  /*
2825  * ExecInitWindowAgg already checked permission to call aggregate function
2826  * ... but we still need to check the component functions
2827  */
2828 
2829  /* Check that aggregate owner has permission to call component fns */
2830  {
2831  HeapTuple procTuple;
2832  Oid aggOwner;
2833 
2834  procTuple = SearchSysCache1(PROCOID,
2835  ObjectIdGetDatum(wfunc->winfnoid));
2836  if (!HeapTupleIsValid(procTuple))
2837  elog(ERROR, "cache lookup failed for function %u",
2838  wfunc->winfnoid);
2839  aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
2840  ReleaseSysCache(procTuple);
2841 
2842  aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
2843  ACL_EXECUTE);
2844  if (aclresult != ACLCHECK_OK)
2845  aclcheck_error(aclresult, OBJECT_FUNCTION,
2846  get_func_name(transfn_oid));
2847  InvokeFunctionExecuteHook(transfn_oid);
2848 
2849  if (OidIsValid(invtransfn_oid))
2850  {
2851  aclresult = pg_proc_aclcheck(invtransfn_oid, aggOwner,
2852  ACL_EXECUTE);
2853  if (aclresult != ACLCHECK_OK)
2854  aclcheck_error(aclresult, OBJECT_FUNCTION,
2855  get_func_name(invtransfn_oid));
2856  InvokeFunctionExecuteHook(invtransfn_oid);
2857  }
2858 
2859  if (OidIsValid(finalfn_oid))
2860  {
2861  aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
2862  ACL_EXECUTE);
2863  if (aclresult != ACLCHECK_OK)
2864  aclcheck_error(aclresult, OBJECT_FUNCTION,
2865  get_func_name(finalfn_oid));
2866  InvokeFunctionExecuteHook(finalfn_oid);
2867  }
2868  }
2869 
2870  /*
2871  * If the selected finalfn isn't read-only, we can't run this aggregate as
2872  * a window function. This is a user-facing error, so we take a bit more
2873  * care with the error message than elsewhere in this function.
2874  */
2875  if (finalmodify != AGGMODIFY_READ_ONLY)
2876  ereport(ERROR,
2877  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2878  errmsg("aggregate function %s does not support use as a window function",
2879  format_procedure(wfunc->winfnoid))));
2880 
2881  /* Detect how many arguments to pass to the finalfn */
2882  if (finalextra)
2883  peraggstate->numFinalArgs = numArguments + 1;
2884  else
2885  peraggstate->numFinalArgs = 1;
2886 
2887  /* resolve actual type of transition state, if polymorphic */
2888  aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
2889  aggtranstype,
2890  inputTypes,
2891  numArguments);
2892 
2893  /* build expression trees using actual argument & result types */
2894  build_aggregate_transfn_expr(inputTypes,
2895  numArguments,
2896  0, /* no ordered-set window functions yet */
2897  false, /* no variadic window functions yet */
2898  aggtranstype,
2899  wfunc->inputcollid,
2900  transfn_oid,
2901  invtransfn_oid,
2902  &transfnexpr,
2903  &invtransfnexpr);
2904 
2905  /* set up infrastructure for calling the transfn(s) and finalfn */
2906  fmgr_info(transfn_oid, &peraggstate->transfn);
2907  fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
2908 
2909  if (OidIsValid(invtransfn_oid))
2910  {
2911  fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
2912  fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
2913  }
2914 
2915  if (OidIsValid(finalfn_oid))
2916  {
2917  build_aggregate_finalfn_expr(inputTypes,
2918  peraggstate->numFinalArgs,
2919  aggtranstype,
2920  wfunc->wintype,
2921  wfunc->inputcollid,
2922  finalfn_oid,
2923  &finalfnexpr);
2924  fmgr_info(finalfn_oid, &peraggstate->finalfn);
2925  fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
2926  }
2927 
2928  /* get info about relevant datatypes */
2929  get_typlenbyval(wfunc->wintype,
2930  &peraggstate->resulttypeLen,
2931  &peraggstate->resulttypeByVal);
2932  get_typlenbyval(aggtranstype,
2933  &peraggstate->transtypeLen,
2934  &peraggstate->transtypeByVal);
2935 
2936  /*
2937  * initval is potentially null, so don't try to access it as a struct
2938  * field. Must do it the hard way with SysCacheGetAttr.
2939  */
2940  textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
2941  &peraggstate->initValueIsNull);
2942 
2943  if (peraggstate->initValueIsNull)
2944  peraggstate->initValue = (Datum) 0;
2945  else
2946  peraggstate->initValue = GetAggInitVal(textInitVal,
2947  aggtranstype);
2948 
2949  /*
2950  * If the transfn is strict and the initval is NULL, make sure input type
2951  * and transtype are the same (or at least binary-compatible), so that
2952  * it's OK to use the first input value as the initial transValue. This
2953  * should have been checked at agg definition time, but we must check
2954  * again in case the transfn's strictness property has been changed.
2955  */
2956  if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
2957  {
2958  if (numArguments < 1 ||
2959  !IsBinaryCoercible(inputTypes[0], aggtranstype))
2960  ereport(ERROR,
2961  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2962  errmsg("aggregate %u needs to have compatible input type and transition type",
2963  wfunc->winfnoid)));
2964  }
2965 
2966  /*
2967  * Insist that forward and inverse transition functions have the same
2968  * strictness setting. Allowing them to differ would require handling
2969  * more special cases in advance_windowaggregate and
2970  * advance_windowaggregate_base, for no discernible benefit. This should
2971  * have been checked at agg definition time, but we must check again in
2972  * case either function's strictness property has been changed.
2973  */
2974  if (OidIsValid(invtransfn_oid) &&
2975  peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
2976  ereport(ERROR,
2977  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2978  errmsg("strictness of aggregate's forward and inverse transition functions must match")));
2979 
2980  /*
2981  * Moving aggregates use their own aggcontext.
2982  *
2983  * This is necessary because they might restart at different times, so we
2984  * might never be able to reset the shared context otherwise. We can't
2985  * make it the aggregates' responsibility to clean up after themselves,
2986  * because strict aggregates must be restarted whenever we remove their
2987  * last non-NULL input, which the aggregate won't be aware is happening.
2988  * Also, just pfree()ing the transValue upon restarting wouldn't help,
2989  * since we'd miss any indirectly referenced data. We could, in theory,
2990  * make the memory allocation rules for moving aggregates different than
2991  * they have historically been for plain aggregates, but that seems grotty
2992  * and likely to lead to memory leaks.
2993  */
2994  if (OidIsValid(invtransfn_oid))
2995  peraggstate->aggcontext =
2997  "WindowAgg Per Aggregate",
2999  else
3000  peraggstate->aggcontext = winstate->aggcontext;
3001 
3002  ReleaseSysCache(aggTuple);
3003 
3004  return peraggstate;
3005 }
3006 
3007 static Datum
3008 GetAggInitVal(Datum textInitVal, Oid transtype)
3009 {
3010  Oid typinput,
3011  typioparam;
3012  char *strInitVal;
3013  Datum initVal;
3014 
3015  getTypeInputInfo(transtype, &typinput, &typioparam);
3016  strInitVal = TextDatumGetCString(textInitVal);
3017  initVal = OidInputFunctionCall(typinput, strInitVal,
3018  typioparam, -1);
3019  pfree(strInitVal);
3020  return initVal;
3021 }
3022 
3023 /*
3024  * are_peers
3025  * compare two rows to see if they are equal according to the ORDER BY clause
3026  *
3027  * NB: this does not consider the window frame mode.
3028  */
3029 static bool
3031  TupleTableSlot *slot2)
3032 {
3033  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3034  ExprContext *econtext = winstate->tmpcontext;
3035 
3036  /* If no ORDER BY, all rows are peers with each other */
3037  if (node->ordNumCols == 0)
3038  return true;
3039 
3040  econtext->ecxt_outertuple = slot1;
3041  econtext->ecxt_innertuple = slot2;
3042  return ExecQualAndReset(winstate->ordEqfunction, econtext);
3043 }
3044 
3045 /*
3046  * window_gettupleslot
3047  * Fetch the pos'th tuple of the current partition into the slot,
3048  * using the winobj's read pointer
3049  *
3050  * Returns true if successful, false if no such row
3051  */
3052 static bool
3054 {
3055  WindowAggState *winstate = winobj->winstate;
3056  MemoryContext oldcontext;
3057 
3058  /* often called repeatedly in a row */
3060 
3061  /* Don't allow passing -1 to spool_tuples here */
3062  if (pos < 0)
3063  return false;
3064 
3065  /* If necessary, fetch the tuple into the spool */
3066  spool_tuples(winstate, pos);
3067 
3068  if (pos >= winstate->spooled_rows)
3069  return false;
3070 
3071  if (pos < winobj->markpos)
3072  elog(ERROR, "cannot fetch row before WindowObject's mark position");
3073 
3075 
3076  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3077 
3078  /*
3079  * Advance or rewind until we are within one tuple of the one we want.
3080  */
3081  if (winobj->seekpos < pos - 1)
3082  {
3083  if (!tuplestore_skiptuples(winstate->buffer,
3084  pos - 1 - winobj->seekpos,
3085  true))
3086  elog(ERROR, "unexpected end of tuplestore");
3087  winobj->seekpos = pos - 1;
3088  }
3089  else if (winobj->seekpos > pos + 1)
3090  {
3091  if (!tuplestore_skiptuples(winstate->buffer,
3092  winobj->seekpos - (pos + 1),
3093  false))
3094  elog(ERROR, "unexpected end of tuplestore");
3095  winobj->seekpos = pos + 1;
3096  }
3097  else if (winobj->seekpos == pos)
3098  {
3099  /*
3100  * There's no API to refetch the tuple at the current position. We
3101  * have to move one tuple forward, and then one backward. (We don't
3102  * do it the other way because we might try to fetch the row before
3103  * our mark, which isn't allowed.) XXX this case could stand to be
3104  * optimized.
3105  */
3106  tuplestore_advance(winstate->buffer, true);
3107  winobj->seekpos++;
3108  }
3109 
3110  /*
3111  * Now we should be on the tuple immediately before or after the one we
3112  * want, so just fetch forwards or backwards as appropriate.
3113  */
3114  if (winobj->seekpos > pos)
3115  {
3116  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3117  elog(ERROR, "unexpected end of tuplestore");
3118  winobj->seekpos--;
3119  }
3120  else
3121  {
3122  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3123  elog(ERROR, "unexpected end of tuplestore");
3124  winobj->seekpos++;
3125  }
3126 
3127  Assert(winobj->seekpos == pos);
3128 
3129  MemoryContextSwitchTo(oldcontext);
3130 
3131  return true;
3132 }
3133 
3134 
3135 /***********************************************************************
3136  * API exposed to window functions
3137  ***********************************************************************/
3138 
3139 
3140 /*
3141  * WinGetPartitionLocalMemory
3142  * Get working memory that lives till end of partition processing
3143  *
3144  * On first call within a given partition, this allocates and zeroes the
3145  * requested amount of space. Subsequent calls just return the same chunk.
3146  *
3147  * Memory obtained this way is normally used to hold state that should be
3148  * automatically reset for each new partition. If a window function wants
3149  * to hold state across the whole query, fcinfo->fn_extra can be used in the
3150  * usual way for that.
3151  */
3152 void *
3154 {
3155  Assert(WindowObjectIsValid(winobj));
3156  if (winobj->localmem == NULL)
3157  winobj->localmem =
3159  return winobj->localmem;
3160 }
3161 
3162 /*
3163  * WinGetCurrentPosition
3164  * Return the current row's position (counting from 0) within the current
3165  * partition.
3166  */
3167 int64
3169 {
3170  Assert(WindowObjectIsValid(winobj));
3171  return winobj->winstate->currentpos;
3172 }
3173 
3174 /*
3175  * WinGetPartitionRowCount
3176  * Return total number of rows contained in the current partition.
3177  *
3178  * Note: this is a relatively expensive operation because it forces the
3179  * whole partition to be "spooled" into the tuplestore at once. Once
3180  * executed, however, additional calls within the same partition are cheap.
3181  */
3182 int64
3184 {
3185  Assert(WindowObjectIsValid(winobj));
3186  spool_tuples(winobj->winstate, -1);
3187  return winobj->winstate->spooled_rows;
3188 }
3189 
3190 /*
3191  * WinSetMarkPosition
3192  * Set the "mark" position for the window object, which is the oldest row
3193  * number (counting from 0) it is allowed to fetch during all subsequent
3194  * operations within the current partition.
3195  *
3196  * Window functions do not have to call this, but are encouraged to move the
3197  * mark forward when possible to keep the tuplestore size down and prevent
3198  * having to spill rows to disk.
3199  */
3200 void
3201 WinSetMarkPosition(WindowObject winobj, int64 markpos)
3202 {
3203  WindowAggState *winstate;
3204 
3205  Assert(WindowObjectIsValid(winobj));
3206  winstate = winobj->winstate;
3207 
3208  if (markpos < winobj->markpos)
3209  elog(ERROR, "cannot move WindowObject's mark position backward");
3210  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3211  if (markpos > winobj->markpos)
3212  {
3213  tuplestore_skiptuples(winstate->buffer,
3214  markpos - winobj->markpos,
3215  true);
3216  winobj->markpos = markpos;
3217  }
3218  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3219  if (markpos > winobj->seekpos)
3220  {
3221  tuplestore_skiptuples(winstate->buffer,
3222  markpos - winobj->seekpos,
3223  true);
3224  winobj->seekpos = markpos;
3225  }
3226 }
3227 
3228 /*
3229  * WinRowsArePeers
3230  * Compare two rows (specified by absolute position in partition) to see
3231  * if they are equal according to the ORDER BY clause.
3232  *
3233  * NB: this does not consider the window frame mode.
3234  */
3235 bool
3236 WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
3237 {
3238  WindowAggState *winstate;
3239  WindowAgg *node;
3240  TupleTableSlot *slot1;
3241  TupleTableSlot *slot2;
3242  bool res;
3243 
3244  Assert(WindowObjectIsValid(winobj));
3245  winstate = winobj->winstate;
3246  node = (WindowAgg *) winstate->ss.ps.plan;
3247 
3248  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3249  if (node->ordNumCols == 0)
3250  return true;
3251 
3252  /*
3253  * Note: OK to use temp_slot_2 here because we aren't calling any
3254  * frame-related functions (those tend to clobber temp_slot_2).
3255  */
3256  slot1 = winstate->temp_slot_1;
3257  slot2 = winstate->temp_slot_2;
3258 
3259  if (!window_gettupleslot(winobj, pos1, slot1))
3260  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3261  pos1);
3262  if (!window_gettupleslot(winobj, pos2, slot2))
3263  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3264  pos2);
3265 
3266  res = are_peers(winstate, slot1, slot2);
3267 
3268  ExecClearTuple(slot1);
3269  ExecClearTuple(slot2);
3270 
3271  return res;
3272 }
3273 
3274 /*
3275  * WinGetFuncArgInPartition
3276  * Evaluate a window function's argument expression on a specified
3277  * row of the partition. The row is identified in lseek(2) style,
3278  * i.e. relative to the current, first, or last row.
3279  *
3280  * argno: argument number to evaluate (counted from 0)
3281  * relpos: signed rowcount offset from the seek position
3282  * seektype: WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, or WINDOW_SEEK_TAIL
3283  * set_mark: If the row is found and set_mark is true, the mark is moved to
3284  * the row as a side-effect.
3285  * isnull: output argument, receives isnull status of result
3286  * isout: output argument, set to indicate whether target row position
3287  * is out of partition (can pass NULL if caller doesn't care about this)
3288  *
3289  * Specifying a nonexistent row is not an error, it just causes a null result
3290  * (plus setting *isout true, if isout isn't NULL).
3291  */
3292 Datum
3294  int relpos, int seektype, bool set_mark,
3295  bool *isnull, bool *isout)
3296 {
3297  WindowAggState *winstate;
3298  ExprContext *econtext;
3299  TupleTableSlot *slot;
3300  bool gottuple;
3301  int64 abs_pos;
3302 
3303  Assert(WindowObjectIsValid(winobj));
3304  winstate = winobj->winstate;
3305  econtext = winstate->ss.ps.ps_ExprContext;
3306  slot = winstate->temp_slot_1;
3307 
3308  switch (seektype)
3309  {
3310  case WINDOW_SEEK_CURRENT:
3311  abs_pos = winstate->currentpos + relpos;
3312  break;
3313  case WINDOW_SEEK_HEAD:
3314  abs_pos = relpos;
3315  break;
3316  case WINDOW_SEEK_TAIL:
3317  spool_tuples(winstate, -1);
3318  abs_pos = winstate->spooled_rows - 1 + relpos;
3319  break;
3320  default:
3321  elog(ERROR, "unrecognized window seek type: %d", seektype);
3322  abs_pos = 0; /* keep compiler quiet */
3323  break;
3324  }
3325 
3326  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3327 
3328  if (!gottuple)
3329  {
3330  if (isout)
3331  *isout = true;
3332  *isnull = true;
3333  return (Datum) 0;
3334  }
3335  else
3336  {
3337  if (isout)
3338  *isout = false;
3339  if (set_mark)
3340  WinSetMarkPosition(winobj, abs_pos);
3341  econtext->ecxt_outertuple = slot;
3342  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3343  econtext, isnull);
3344  }
3345 }
3346 
3347 /*
3348  * WinGetFuncArgInFrame
3349  * Evaluate a window function's argument expression on a specified
3350  * row of the window frame. The row is identified in lseek(2) style,
3351  * i.e. relative to the first or last row of the frame. (We do not
3352  * support WINDOW_SEEK_CURRENT here, because it's not very clear what
3353  * that should mean if the current row isn't part of the frame.)
3354  *
3355  * argno: argument number to evaluate (counted from 0)
3356  * relpos: signed rowcount offset from the seek position
3357  * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
3358  * set_mark: If the row is found/in frame and set_mark is true, the mark is
3359  * moved to the row as a side-effect.
3360  * isnull: output argument, receives isnull status of result
3361  * isout: output argument, set to indicate whether target row position
3362  * is out of frame (can pass NULL if caller doesn't care about this)
3363  *
3364  * Specifying a nonexistent or not-in-frame row is not an error, it just
3365  * causes a null result (plus setting *isout true, if isout isn't NULL).
3366  *
3367  * Note that some exclusion-clause options lead to situations where the
3368  * rows that are in-frame are not consecutive in the partition. But we
3369  * count only in-frame rows when measuring relpos.
3370  *
3371  * The set_mark flag is interpreted as meaning that the caller will specify
3372  * a constant (or, perhaps, monotonically increasing) relpos in successive
3373  * calls, so that *if there is no exclusion clause* there will be no need
3374  * to fetch a row before the previously fetched row. But we do not expect
3375  * the caller to know how to account for exclusion clauses. Therefore,
3376  * if there is an exclusion clause we take responsibility for adjusting the
3377  * mark request to something that will be safe given the above assumption
3378  * about relpos.
3379  */
3380 Datum
3382  int relpos, int seektype, bool set_mark,
3383  bool *isnull, bool *isout)
3384 {
3385  WindowAggState *winstate;
3386  ExprContext *econtext;
3387  TupleTableSlot *slot;
3388  int64 abs_pos;
3389  int64 mark_pos;
3390 
3391  Assert(WindowObjectIsValid(winobj));
3392  winstate = winobj->winstate;
3393  econtext = winstate->ss.ps.ps_ExprContext;
3394  slot = winstate->temp_slot_1;
3395 
3396  switch (seektype)
3397  {
3398  case WINDOW_SEEK_CURRENT:
3399  elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3400  abs_pos = mark_pos = 0; /* keep compiler quiet */
3401  break;
3402  case WINDOW_SEEK_HEAD:
3403  /* rejecting relpos < 0 is easy and simplifies code below */
3404  if (relpos < 0)
3405  goto out_of_frame;
3406  update_frameheadpos(winstate);
3407  abs_pos = winstate->frameheadpos + relpos;
3408  mark_pos = abs_pos;
3409 
3410  /*
3411  * Account for exclusion option if one is active, but advance only
3412  * abs_pos not mark_pos. This prevents changes of the current
3413  * row's peer group from resulting in trying to fetch a row before
3414  * some previous mark position.
3415  *
3416  * Note that in some corner cases such as current row being
3417  * outside frame, these calculations are theoretically too simple,
3418  * but it doesn't matter because we'll end up deciding the row is
3419  * out of frame. We do not attempt to avoid fetching rows past
3420  * end of frame; that would happen in some cases anyway.
3421  */
3422  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3423  {
3424  case 0:
3425  /* no adjustment needed */
3426  break;
3428  if (abs_pos >= winstate->currentpos &&
3429  winstate->currentpos >= winstate->frameheadpos)
3430  abs_pos++;
3431  break;
3433  update_grouptailpos(winstate);
3434  if (abs_pos >= winstate->groupheadpos &&
3435  winstate->grouptailpos > winstate->frameheadpos)
3436  {
3437  int64 overlapstart = Max(winstate->groupheadpos,
3438  winstate->frameheadpos);
3439 
3440  abs_pos += winstate->grouptailpos - overlapstart;
3441  }
3442  break;
3444  update_grouptailpos(winstate);
3445  if (abs_pos >= winstate->groupheadpos &&
3446  winstate->grouptailpos > winstate->frameheadpos)
3447  {
3448  int64 overlapstart = Max(winstate->groupheadpos,
3449  winstate->frameheadpos);
3450 
3451  if (abs_pos == overlapstart)
3452  abs_pos = winstate->currentpos;
3453  else
3454  abs_pos += winstate->grouptailpos - overlapstart - 1;
3455  }
3456  break;
3457  default:
3458  elog(ERROR, "unrecognized frame option state: 0x%x",
3459  winstate->frameOptions);
3460  break;
3461  }
3462  break;
3463  case WINDOW_SEEK_TAIL:
3464  /* rejecting relpos > 0 is easy and simplifies code below */
3465  if (relpos > 0)
3466  goto out_of_frame;
3467  update_frametailpos(winstate);
3468  abs_pos = winstate->frametailpos - 1 + relpos;
3469 
3470  /*
3471  * Account for exclusion option if one is active. If there is no
3472  * exclusion, we can safely set the mark at the accessed row. But
3473  * if there is, we can only mark the frame start, because we can't
3474  * be sure how far back in the frame the exclusion might cause us
3475  * to fetch in future. Furthermore, we have to actually check
3476  * against frameheadpos here, since it's unsafe to try to fetch a
3477  * row before frame start if the mark might be there already.
3478  */
3479  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3480  {
3481  case 0:
3482  /* no adjustment needed */
3483  mark_pos = abs_pos;
3484  break;
3486  if (abs_pos <= winstate->currentpos &&
3487  winstate->currentpos < winstate->frametailpos)
3488  abs_pos--;
3489  update_frameheadpos(winstate);
3490  if (abs_pos < winstate->frameheadpos)
3491  goto out_of_frame;
3492  mark_pos = winstate->frameheadpos;
3493  break;
3495  update_grouptailpos(winstate);
3496  if (abs_pos < winstate->grouptailpos &&
3497  winstate->groupheadpos < winstate->frametailpos)
3498  {
3499  int64 overlapend = Min(winstate->grouptailpos,
3500  winstate->frametailpos);
3501 
3502  abs_pos -= overlapend - winstate->groupheadpos;
3503  }
3504  update_frameheadpos(winstate);
3505  if (abs_pos < winstate->frameheadpos)
3506  goto out_of_frame;
3507  mark_pos = winstate->frameheadpos;
3508  break;
3510  update_grouptailpos(winstate);
3511  if (abs_pos < winstate->grouptailpos &&
3512  winstate->groupheadpos < winstate->frametailpos)
3513  {
3514  int64 overlapend = Min(winstate->grouptailpos,
3515  winstate->frametailpos);
3516 
3517  if (abs_pos == overlapend - 1)
3518  abs_pos = winstate->currentpos;
3519  else
3520  abs_pos -= overlapend - 1 - winstate->groupheadpos;
3521  }
3522  update_frameheadpos(winstate);
3523  if (abs_pos < winstate->frameheadpos)
3524  goto out_of_frame;
3525  mark_pos = winstate->frameheadpos;
3526  break;
3527  default:
3528  elog(ERROR, "unrecognized frame option state: 0x%x",
3529  winstate->frameOptions);
3530  mark_pos = 0; /* keep compiler quiet */
3531  break;
3532  }
3533  break;
3534  default:
3535  elog(ERROR, "unrecognized window seek type: %d", seektype);
3536  abs_pos = mark_pos = 0; /* keep compiler quiet */
3537  break;
3538  }
3539 
3540  if (!window_gettupleslot(winobj, abs_pos, slot))
3541  goto out_of_frame;
3542 
3543  /* The code above does not detect all out-of-frame cases, so check */
3544  if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3545  goto out_of_frame;
3546 
3547  if (isout)
3548  *isout = false;
3549  if (set_mark)
3550  WinSetMarkPosition(winobj, mark_pos);
3551  econtext->ecxt_outertuple = slot;
3552  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3553  econtext, isnull);
3554 
3555 out_of_frame:
3556  if (isout)
3557  *isout = true;
3558  *isnull = true;
3559  return (Datum) 0;
3560 }
3561 
3562 /*
3563  * WinGetFuncArgCurrent
3564  * Evaluate a window function's argument expression on the current row.
3565  *
3566  * argno: argument number to evaluate (counted from 0)
3567  * isnull: output argument, receives isnull status of result
3568  *
3569  * Note: this isn't quite equivalent to WinGetFuncArgInPartition or
3570  * WinGetFuncArgInFrame targeting the current row, because it will succeed
3571  * even if the WindowObject's mark has been set beyond the current row.
3572  * This should generally be used for "ordinary" arguments of a window
3573  * function, such as the offset argument of lead() or lag().
3574  */
3575 Datum
3576 WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
3577 {
3578  WindowAggState *winstate;
3579  ExprContext *econtext;
3580 
3581  Assert(WindowObjectIsValid(winobj));
3582  winstate = winobj->winstate;
3583 
3584  econtext = winstate->ss.ps.ps_ExprContext;
3585 
3586  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3587  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3588  econtext, isnull);
3589 }
AclResult
Definition: acl.h:181
@ ACLCHECK_OK
Definition: acl.h:182
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3512
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:5071
int16 AttrNumber
Definition: attnum.h:21
#define TextDatumGetCString(d)
Definition: builtins.h:86
#define Min(x, y)
Definition: c.h:986
signed short int16
Definition: c.h:428
#define Max(x, y)
Definition: c.h:980
#define INT64_FORMAT
Definition: c.h:483
#define MemSet(start, val, len)
Definition: c.h:1008
#define OidIsValid(objectId)
Definition: c.h:710
size_t Size
Definition: c.h:540
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:496
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define ereport(elevel,...)
Definition: elog.h:143
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3564
void ExecReScan(PlanState *node)
Definition: execAmi.c:78
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:209
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:160
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:59
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:556
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:141
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1831
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1799
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition: execUtils.c:682
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:535
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:2407
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1102
#define outerPlanState(node)
Definition: execnodes.h:1094
@ WINDOWAGG_PASSTHROUGH
Definition: execnodes.h:2416
@ WINDOWAGG_RUN
Definition: execnodes.h:2415
@ WINDOWAGG_DONE
Definition: execnodes.h:2414
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition: execnodes.h:2417
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:2406
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:363
#define ResetExprContext(econtext)
Definition: executor.h:531
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:400
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:427
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:320
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:335
#define EXEC_FLAG_MARK
Definition: executor.h:59
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:254
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
void DeleteExpandedObject(Datum d)
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1630
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1208
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:136
#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
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:135
int work_mem
Definition: globals.c:125
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
static struct @151 value
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2208
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2831
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1589
void pfree(void *pointer)
Definition: mcxt.c:1175
void * palloc0(Size size)
Definition: mcxt.c:1099
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:906
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
bool MemoryContextContains(MemoryContext context, void *pointer)
Definition: mcxt.c:758
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:446
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:197
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
Oid GetUserId(void)
Definition: miscinit.c:492
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
Datum WinGetFuncArgInPartition(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
static void begin_partition(WindowAggState *winstate)
struct WindowObjectData WindowObjectData
static void update_grouptailpos(WindowAggState *winstate)
Datum WinGetFuncArgInFrame(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
WindowAggState * ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
struct WindowStatePerAggData WindowStatePerAggData
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
static void spool_tuples(WindowAggState *winstate, int64 pos)
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
void ExecEndWindowAgg(WindowAggState *node)
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
struct WindowStatePerFuncData WindowStatePerFuncData
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
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 ExecReScanWindowAgg(WindowAggState *node)
int64 WinGetCurrentPosition(WindowObject winobj)
bool WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
static void eval_windowaggregates(WindowAggState *winstate)
static void release_partition(WindowAggState *winstate)
void * WinGetPartitionLocalMemory(WindowObject winobj, Size sz)
static void update_frametailpos(WindowAggState *winstate)
static void update_frameheadpos(WindowAggState *winstate)
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
Datum WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
int64 WinGetPartitionRowCount(WindowObject winobj)
NodeTag
Definition: nodes.h:27
#define makeNode(_type_)
Definition: nodes.h:621
#define castNode(_type_, nodeptr)
Definition: nodes.h:642
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:211
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
void build_aggregate_finalfn_expr(Oid *agg_input_types, int num_finalfn_inputs, Oid agg_state_type, Oid agg_result_type, Oid agg_input_collation, Oid finalfn_oid, Expr **finalfnexpr)
Definition: parse_agg.c:2084
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:1916
void build_aggregate_transfn_expr(Oid *agg_input_types, int agg_num_inputs, int agg_num_direct_inputs, bool agg_variadic, Oid agg_state_type, Oid agg_input_collation, Oid transfn_oid, Oid invtransfn_oid, Expr **transfnexpr, Expr **invtransfnexpr)
Definition: parse_agg.c:1976
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:545
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:556
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:550
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:547
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:540
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:544
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:554
@ OBJECT_FUNCTION
Definition: parsenodes.h:2153
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:552
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:536
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:551
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:538
#define ACL_EXECUTE
Definition: parsenodes.h:89
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:543
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:546
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:558
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:537
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
void * arg
#define FUNC_MAX_ARGS
const void size_t len
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define NIL
Definition: pg_list.h:65
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
#define outerPlan(node)
Definition: plannodes.h:172
uintptr_t Datum
Definition: postgres.h:411
#define DatumGetBool(X)
Definition: postgres.h:437
#define DatumGetPointer(X)
Definition: postgres.h:593
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define BoolGetDatum(X)
Definition: postgres.h:446
#define DatumGetInt64(X)
Definition: postgres.h:651
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:240
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:234
Datum * ecxt_aggvalues
Definition: execnodes.h:251
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:232
bool * ecxt_aggnulls
Definition: execnodes.h:253
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:239
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:236
Expr * expr
Definition: execnodes.h:95
Definition: fmgr.h:57
bool fn_strict
Definition: fmgr.h:61
Definition: pg_list.h:51
Definition: nodes.h:574
bool outeropsset
Definition: execnodes.h:1081
const TupleTableSlotOps * outerops
Definition: execnodes.h:1073
ExprState * qual
Definition: execnodes.h:1019
Plan * plan
Definition: execnodes.h:998
bool outeropsfixed
Definition: execnodes.h:1077
EState * state
Definition: execnodes.h:1000
ExprContext * ps_ExprContext
Definition: execnodes.h:1037
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1038
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1004
List * qual
Definition: plannodes.h:143
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1426
PlanState ps
Definition: execnodes.h:1423
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
ExprState * endOffset
Definition: execnodes.h:2451
MemoryContext aggcontext
Definition: execnodes.h:2470
ScanState ss
Definition: execnodes.h:2423
int64 aggregatedbase
Definition: execnodes.h:2445
int64 frametailgroup
Definition: execnodes.h:2465
int64 frameheadgroup
Definition: execnodes.h:2464
WindowStatePerAgg peragg
Definition: execnodes.h:2431
MemoryContext partcontext
Definition: execnodes.h:2469
FmgrInfo endInRangeFunc
Definition: execnodes.h:2457
TupleTableSlot * framehead_slot
Definition: execnodes.h:2498
bool frametail_valid
Definition: execnodes.h:2491
bool partition_spooled
Definition: execnodes.h:2485
FmgrInfo startInRangeFunc
Definition: execnodes.h:2456
int64 spooled_rows
Definition: execnodes.h:2439
int64 frameheadpos
Definition: execnodes.h:2441
bool more_partitions
Definition: execnodes.h:2487
Datum startOffsetValue
Definition: execnodes.h:2452
int64 grouptailpos
Definition: execnodes.h:2467
int64 currentgroup
Definition: execnodes.h:2463
TupleTableSlot * frametail_slot
Definition: execnodes.h:2499
ExprState * ordEqfunction
Definition: execnodes.h:2433
ExprState * runcondition
Definition: execnodes.h:2474
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2504
Tuplestorestate * buffer
Definition: execnodes.h:2434
WindowAggStatus status
Definition: execnodes.h:2447
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2502
struct WindowObjectData * agg_winobj
Definition: execnodes.h:2444
WindowStatePerFunc perfunc
Definition: execnodes.h:2430
bool framehead_valid
Definition: execnodes.h:2489
int64 groupheadpos
Definition: execnodes.h:2466
MemoryContext curaggcontext
Definition: execnodes.h:2471
bool inRangeNullsFirst
Definition: execnodes.h:2460
bool grouptail_valid
Definition: execnodes.h:2493
Datum endOffsetValue
Definition: execnodes.h:2453
int64 currentpos
Definition: execnodes.h:2440
ExprState * partEqfunction
Definition: execnodes.h:2432
int64 frametailpos
Definition: execnodes.h:2442
ExprState * startOffset
Definition: execnodes.h:2450
TupleTableSlot * first_part_slot
Definition: execnodes.h:2496
int64 aggregatedupto
Definition: execnodes.h:2446
ExprContext * tmpcontext
Definition: execnodes.h:2472
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2503
bool use_pass_through
Definition: execnodes.h:2479
Oid * partOperators
Definition: plannodes.h:921
int partNumCols
Definition: plannodes.h:919
Oid endInRangeFunc
Definition: plannodes.h:934
Node * endOffset
Definition: plannodes.h:929
AttrNumber * ordColIdx
Definition: plannodes.h:924
bool topWindow
Definition: plannodes.h:938
Plan plan
Definition: plannodes.h:917
Oid inRangeColl
Definition: plannodes.h:935
Node * startOffset
Definition: plannodes.h:928
List * runCondition
Definition: plannodes.h:930
Oid startInRangeFunc
Definition: plannodes.h:933
bool inRangeAsc
Definition: plannodes.h:936
Oid * partCollations
Definition: plannodes.h:922
Index winref
Definition: plannodes.h:918
bool inRangeNullsFirst
Definition: plannodes.h:937
Oid * ordCollations
Definition: plannodes.h:926
Oid * ordOperators
Definition: plannodes.h:925
int ordNumCols
Definition: plannodes.h:923
AttrNumber * partColIdx
Definition: plannodes.h:920
int frameOptions
Definition: plannodes.h:927
WindowFunc * wfunc
Definition: execnodes.h:826
ExprState * aggfilter
Definition: execnodes.h:828
bool winagg
Definition: primnodes.h:403
Oid inputcollid
Definition: primnodes.h:398
List * args
Definition: primnodes.h:399
Index winref
Definition: primnodes.h:401
Oid winfnoid
Definition: primnodes.h:395
Oid wintype
Definition: primnodes.h:396
WindowAggState * winstate
Definition: nodeWindowAgg.c:64
MemoryContext aggcontext
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:80
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1173
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1434
@ AGGFNOID
Definition: syscache.h:34
@ PROCOID
Definition: syscache.h:79
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:383
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1360
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1110
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1455
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:359
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1135
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:381
#define TupIsNull(slot)
Definition: tuptable.h:292
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32