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-2018, 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/clauses.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/lsyscache.h"
51 #include "utils/memutils.h"
52 #include "utils/regproc.h"
53 #include "utils/syscache.h"
54 #include "windowapi.h"
55 
56 /*
57  * All the window function APIs are called with this object, which is passed
58  * to window functions as fcinfo->context.
59  */
60 typedef struct WindowObjectData
61 {
63  WindowAggState *winstate; /* parent WindowAggState */
64  List *argstates; /* ExprState trees for fn's arguments */
65  void *localmem; /* WinGetPartitionLocalMemory's chunk */
66  int markptr; /* tuplestore mark pointer for this fn */
67  int readptr; /* tuplestore read pointer for this fn */
68  int64 markpos; /* row that markptr is positioned on */
69  int64 seekpos; /* row that readptr is positioned on */
71 
72 /*
73  * We have one WindowStatePerFunc struct for each window function and
74  * window aggregate handled by this node.
75  */
76 typedef struct WindowStatePerFuncData
77 {
78  /* Links to WindowFunc expr and state nodes this working state is for */
81 
82  int numArguments; /* number of arguments */
83 
84  FmgrInfo flinfo; /* fmgr lookup data for window function */
85 
86  Oid winCollation; /* collation derived for window function */
87 
88  /*
89  * We need the len and byval info for the result of each function in order
90  * to know how to copy/delete values.
91  */
94 
95  bool plain_agg; /* is it just a plain aggregate function? */
96  int aggno; /* if so, index of its PerAggData */
97 
98  WindowObject winobj; /* object used in window function API */
100 
101 /*
102  * For plain aggregate window functions, we also have one of these.
103  */
104 typedef struct WindowStatePerAggData
105 {
106  /* Oids of transition functions */
108  Oid invtransfn_oid; /* may be InvalidOid */
109  Oid finalfn_oid; /* may be InvalidOid */
110 
111  /*
112  * fmgr lookup data for transition functions --- only valid when
113  * corresponding oid is not InvalidOid. Note in particular that fn_strict
114  * flags are kept here.
115  */
119 
120  int numFinalArgs; /* number of arguments to pass to finalfn */
121 
122  /*
123  * initial value from pg_aggregate entry
124  */
127 
128  /*
129  * cached value for current frame boundaries
130  */
133 
134  /*
135  * We need the len and byval info for the agg's input, result, and
136  * transition data types in order to know how to copy/delete values.
137  */
138  int16 inputtypeLen,
139  resulttypeLen,
140  transtypeLen;
141  bool inputtypeByVal,
142  resulttypeByVal,
144 
145  int wfuncno; /* index of associated PerFuncData */
146 
147  /* Context holding transition value and possibly other subsidiary data */
148  MemoryContext aggcontext; /* may be private, or winstate->aggcontext */
149 
150  /* Current transition value */
151  Datum transValue; /* current transition value */
153 
154  int64 transValueCount; /* number of currently-aggregated rows */
155 
156  /* Data local to eval_windowaggregates() */
157  bool restart; /* need to restart this agg in this cycle? */
159 
161  WindowStatePerFunc perfuncstate,
162  WindowStatePerAgg peraggstate);
164  WindowStatePerFunc perfuncstate,
165  WindowStatePerAgg peraggstate);
167  WindowStatePerFunc perfuncstate,
168  WindowStatePerAgg peraggstate);
170  WindowStatePerFunc perfuncstate,
171  WindowStatePerAgg peraggstate,
172  Datum *result, bool *isnull);
173 
176  WindowStatePerFunc perfuncstate,
177  Datum *result, bool *isnull);
178 
180 static void spool_tuples(WindowAggState *winstate, int64 pos);
182 
183 static int row_is_in_frame(WindowAggState *winstate, int64 pos,
184  TupleTableSlot *slot);
188 
190  WindowFunc *wfunc,
191  WindowStatePerAgg peraggstate);
192 static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
193 
194 static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
195  TupleTableSlot *slot2);
196 static bool window_gettupleslot(WindowObject winobj, int64 pos,
197  TupleTableSlot *slot);
198 
199 
200 /*
201  * initialize_windowaggregate
202  * parallel to initialize_aggregates in nodeAgg.c
203  */
204 static void
206  WindowStatePerFunc perfuncstate,
207  WindowStatePerAgg peraggstate)
208 {
209  MemoryContext oldContext;
210 
211  /*
212  * If we're using a private aggcontext, we may reset it here. But if the
213  * context is shared, we don't know which other aggregates may still need
214  * it, so we must leave it to the caller to reset at an appropriate time.
215  */
216  if (peraggstate->aggcontext != winstate->aggcontext)
218 
219  if (peraggstate->initValueIsNull)
220  peraggstate->transValue = peraggstate->initValue;
221  else
222  {
223  oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
224  peraggstate->transValue = datumCopy(peraggstate->initValue,
225  peraggstate->transtypeByVal,
226  peraggstate->transtypeLen);
227  MemoryContextSwitchTo(oldContext);
228  }
229  peraggstate->transValueIsNull = peraggstate->initValueIsNull;
230  peraggstate->transValueCount = 0;
231  peraggstate->resultValue = (Datum) 0;
232  peraggstate->resultValueIsNull = true;
233 }
234 
235 /*
236  * advance_windowaggregate
237  * parallel to advance_aggregates in nodeAgg.c
238  */
239 static void
241  WindowStatePerFunc perfuncstate,
242  WindowStatePerAgg peraggstate)
243 {
244  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
245  int numArguments = perfuncstate->numArguments;
246  FunctionCallInfoData fcinfodata;
247  FunctionCallInfo fcinfo = &fcinfodata;
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->arg[i] = ExecEvalExpr(argstate, econtext,
277  &fcinfo->argnull[i]);
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->argnull[i])
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->arg[1],
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->arg[0] = peraggstate->transValue;
343  fcinfo->argnull[0] = 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  WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
422  int numArguments = perfuncstate->numArguments;
423  FunctionCallInfoData fcinfodata;
424  FunctionCallInfo fcinfo = &fcinfodata;
425  Datum newVal;
426  ListCell *arg;
427  int i;
428  MemoryContext oldContext;
429  ExprContext *econtext = winstate->tmpcontext;
430  ExprState *filter = wfuncstate->aggfilter;
431 
432  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
433 
434  /* Skip anything FILTERed out */
435  if (filter)
436  {
437  bool isnull;
438  Datum res = ExecEvalExpr(filter, econtext, &isnull);
439 
440  if (isnull || !DatumGetBool(res))
441  {
442  MemoryContextSwitchTo(oldContext);
443  return true;
444  }
445  }
446 
447  /* We start from 1, since the 0th arg will be the transition value */
448  i = 1;
449  foreach(arg, wfuncstate->args)
450  {
451  ExprState *argstate = (ExprState *) lfirst(arg);
452 
453  fcinfo->arg[i] = ExecEvalExpr(argstate, econtext,
454  &fcinfo->argnull[i]);
455  i++;
456  }
457 
458  if (peraggstate->invtransfn.fn_strict)
459  {
460  /*
461  * For a strict (inv)transfn, nothing happens when there's a NULL
462  * input; we just keep the prior transValue. Note transValueCount
463  * doesn't change either.
464  */
465  for (i = 1; i <= numArguments; i++)
466  {
467  if (fcinfo->argnull[i])
468  {
469  MemoryContextSwitchTo(oldContext);
470  return true;
471  }
472  }
473  }
474 
475  /* There should still be an added but not yet removed value */
476  Assert(peraggstate->transValueCount > 0);
477 
478  /*
479  * In moving-aggregate mode, the state must never be NULL, except possibly
480  * before any rows have been aggregated (which is surely not the case at
481  * this point). This restriction allows us to interpret a NULL result
482  * from the inverse function as meaning "sorry, can't do an inverse
483  * transition in this case". We already checked this in
484  * advance_windowaggregate, but just for safety, check again.
485  */
486  if (peraggstate->transValueIsNull)
487  elog(ERROR, "aggregate transition value is NULL before inverse transition");
488 
489  /*
490  * We mustn't use the inverse transition function to remove the last
491  * input. Doing so would yield a non-NULL state, whereas we should be in
492  * the initial state afterwards which may very well be NULL. So instead,
493  * we simply re-initialize the aggregate in this case.
494  */
495  if (peraggstate->transValueCount == 1)
496  {
497  MemoryContextSwitchTo(oldContext);
499  &winstate->perfunc[peraggstate->wfuncno],
500  peraggstate);
501  return true;
502  }
503 
504  /*
505  * OK to call the inverse transition function. Set
506  * winstate->curaggcontext while calling it, for possible use by
507  * AggCheckCallContext.
508  */
509  InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
510  numArguments + 1,
511  perfuncstate->winCollation,
512  (void *) winstate, NULL);
513  fcinfo->arg[0] = peraggstate->transValue;
514  fcinfo->argnull[0] = peraggstate->transValueIsNull;
515  winstate->curaggcontext = peraggstate->aggcontext;
516  newVal = FunctionCallInvoke(fcinfo);
517  winstate->curaggcontext = NULL;
518 
519  /*
520  * If the function returns NULL, report failure, forcing a restart.
521  */
522  if (fcinfo->isnull)
523  {
524  MemoryContextSwitchTo(oldContext);
525  return false;
526  }
527 
528  /* Update number of rows included in transValue */
529  peraggstate->transValueCount--;
530 
531  /*
532  * If pass-by-ref datatype, must copy the new value into aggcontext and
533  * free the prior transValue. But if invtransfn returned a pointer to its
534  * first input, we don't need to do anything. Also, if invtransfn
535  * returned a pointer to a R/W expanded object that is already a child of
536  * the aggcontext, assume we can adopt that value without copying it.
537  *
538  * Note: the checks for null values here will never fire, but it seems
539  * best to have this stanza look just like advance_windowaggregate.
540  */
541  if (!peraggstate->transtypeByVal &&
542  DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
543  {
544  if (!fcinfo->isnull)
545  {
546  MemoryContextSwitchTo(peraggstate->aggcontext);
548  false,
549  peraggstate->transtypeLen) &&
551  /* do nothing */ ;
552  else
553  newVal = datumCopy(newVal,
554  peraggstate->transtypeByVal,
555  peraggstate->transtypeLen);
556  }
557  if (!peraggstate->transValueIsNull)
558  {
560  false,
561  peraggstate->transtypeLen))
562  DeleteExpandedObject(peraggstate->transValue);
563  else
564  pfree(DatumGetPointer(peraggstate->transValue));
565  }
566  }
567 
568  MemoryContextSwitchTo(oldContext);
569  peraggstate->transValue = newVal;
570  peraggstate->transValueIsNull = fcinfo->isnull;
571 
572  return true;
573 }
574 
575 /*
576  * finalize_windowaggregate
577  * parallel to finalize_aggregate in nodeAgg.c
578  */
579 static void
581  WindowStatePerFunc perfuncstate,
582  WindowStatePerAgg peraggstate,
583  Datum *result, bool *isnull)
584 {
585  MemoryContext oldContext;
586 
588 
589  /*
590  * Apply the agg's finalfn if one is provided, else return transValue.
591  */
592  if (OidIsValid(peraggstate->finalfn_oid))
593  {
594  int numFinalArgs = peraggstate->numFinalArgs;
595  FunctionCallInfoData fcinfo;
596  bool anynull;
597  int i;
598 
599  InitFunctionCallInfoData(fcinfo, &(peraggstate->finalfn),
600  numFinalArgs,
601  perfuncstate->winCollation,
602  (void *) winstate, NULL);
603  fcinfo.arg[0] = MakeExpandedObjectReadOnly(peraggstate->transValue,
604  peraggstate->transValueIsNull,
605  peraggstate->transtypeLen);
606  fcinfo.argnull[0] = 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.arg[i] = (Datum) 0;
613  fcinfo.argnull[i] = 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  FunctionCallInfoData fcinfo;
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  memset(fcinfo.argnull, true, perfuncstate->numArguments);
1052  /* Window functions don't have a current aggregate context, either */
1053  winstate->curaggcontext = NULL;
1054 
1055  *result = FunctionCallInvoke(&fcinfo);
1056  *isnull = fcinfo.isnull;
1057 
1058  /*
1059  * Make sure pass-by-ref data is allocated in the appropriate context. (We
1060  * need this in case the function returns a pointer into some short-lived
1061  * tuple, as is entirely possible.)
1062  */
1063  if (!perfuncstate->resulttypeByVal && !fcinfo.isnull &&
1065  DatumGetPointer(*result)))
1066  *result = datumCopy(*result,
1067  perfuncstate->resulttypeByVal,
1068  perfuncstate->resulttypeLen);
1069 
1070  MemoryContextSwitchTo(oldContext);
1071 }
1072 
1073 /*
1074  * begin_partition
1075  * Start buffering rows of the next partition.
1076  */
1077 static void
1079 {
1080  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1081  PlanState *outerPlan = outerPlanState(winstate);
1082  int numfuncs = winstate->numfuncs;
1083  int i;
1084 
1085  winstate->partition_spooled = false;
1086  winstate->framehead_valid = false;
1087  winstate->frametail_valid = false;
1088  winstate->grouptail_valid = false;
1089  winstate->spooled_rows = 0;
1090  winstate->currentpos = 0;
1091  winstate->frameheadpos = 0;
1092  winstate->frametailpos = 0;
1093  winstate->currentgroup = 0;
1094  winstate->frameheadgroup = 0;
1095  winstate->frametailgroup = 0;
1096  winstate->groupheadpos = 0;
1097  winstate->grouptailpos = -1; /* see update_grouptailpos */
1098  ExecClearTuple(winstate->agg_row_slot);
1099  if (winstate->framehead_slot)
1100  ExecClearTuple(winstate->framehead_slot);
1101  if (winstate->frametail_slot)
1102  ExecClearTuple(winstate->frametail_slot);
1103 
1104  /*
1105  * If this is the very first partition, we need to fetch the first input
1106  * row to store in first_part_slot.
1107  */
1108  if (TupIsNull(winstate->first_part_slot))
1109  {
1110  TupleTableSlot *outerslot = ExecProcNode(outerPlan);
1111 
1112  if (!TupIsNull(outerslot))
1113  ExecCopySlot(winstate->first_part_slot, outerslot);
1114  else
1115  {
1116  /* outer plan is empty, so we have nothing to do */
1117  winstate->partition_spooled = true;
1118  winstate->more_partitions = false;
1119  return;
1120  }
1121  }
1122 
1123  /* Create new tuplestore for this partition */
1124  winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1125 
1126  /*
1127  * Set up read pointers for the tuplestore. The current pointer doesn't
1128  * need BACKWARD capability, but the per-window-function read pointers do,
1129  * and the aggregate pointer does if we might need to restart aggregation.
1130  */
1131  winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1132 
1133  /* reset default REWIND capability bit for current ptr */
1134  tuplestore_set_eflags(winstate->buffer, 0);
1135 
1136  /* create read pointers for aggregates, if needed */
1137  if (winstate->numaggs > 0)
1138  {
1139  WindowObject agg_winobj = winstate->agg_winobj;
1140  int readptr_flags = 0;
1141 
1142  /*
1143  * If the frame head is potentially movable, or we have an EXCLUSION
1144  * clause, we might need to restart aggregation ...
1145  */
1147  (winstate->frameOptions & FRAMEOPTION_EXCLUSION))
1148  {
1149  /* ... so create a mark pointer to track the frame head */
1150  agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1151  /* and the read pointer will need BACKWARD capability */
1152  readptr_flags |= EXEC_FLAG_BACKWARD;
1153  }
1154 
1155  agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1156  readptr_flags);
1157  agg_winobj->markpos = -1;
1158  agg_winobj->seekpos = -1;
1159 
1160  /* Also reset the row counters for aggregates */
1161  winstate->aggregatedbase = 0;
1162  winstate->aggregatedupto = 0;
1163  }
1164 
1165  /* create mark and read pointers for each real window function */
1166  for (i = 0; i < numfuncs; i++)
1167  {
1168  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1169 
1170  if (!perfuncstate->plain_agg)
1171  {
1172  WindowObject winobj = perfuncstate->winobj;
1173 
1174  winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1175  0);
1176  winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1178  winobj->markpos = -1;
1179  winobj->seekpos = -1;
1180  }
1181  }
1182 
1183  /*
1184  * If we are in RANGE or GROUPS mode, then determining frame boundaries
1185  * requires physical access to the frame endpoint rows, except in
1186  * degenerate cases. We create read pointers to point to those rows, to
1187  * simplify access and ensure that the tuplestore doesn't discard the
1188  * endpoint rows prematurely. (Must match logic in update_frameheadpos
1189  * and update_frametailpos.)
1190  */
1191  winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1192 
1193  if ((winstate->frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS)) &&
1194  node->ordNumCols != 0)
1195  {
1197  winstate->framehead_ptr =
1198  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1200  winstate->frametail_ptr =
1201  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1202  }
1203 
1204  /*
1205  * If we have an exclusion clause that requires knowing the boundaries of
1206  * the current row's peer group, we create a read pointer to track the
1207  * tail position of the peer group (i.e., first row of the next peer
1208  * group). The head position does not require its own pointer because we
1209  * maintain that as a side effect of advancing the current row.
1210  */
1211  winstate->grouptail_ptr = -1;
1212 
1213  if ((winstate->frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1215  node->ordNumCols != 0)
1216  {
1217  winstate->grouptail_ptr =
1218  tuplestore_alloc_read_pointer(winstate->buffer, 0);
1219  }
1220 
1221  /*
1222  * Store the first tuple into the tuplestore (it's always available now;
1223  * we either read it above, or saved it at the end of previous partition)
1224  */
1225  tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1226  winstate->spooled_rows++;
1227 }
1228 
1229 /*
1230  * Read tuples from the outer node, up to and including position 'pos', and
1231  * store them into the tuplestore. If pos is -1, reads the whole partition.
1232  */
1233 static void
1235 {
1236  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1238  TupleTableSlot *outerslot;
1239  MemoryContext oldcontext;
1240 
1241  if (!winstate->buffer)
1242  return; /* just a safety check */
1243  if (winstate->partition_spooled)
1244  return; /* whole partition done already */
1245 
1246  /*
1247  * If the tuplestore has spilled to disk, alternate reading and writing
1248  * becomes quite expensive due to frequent buffer flushes. It's cheaper
1249  * to force the entire partition to get spooled in one go.
1250  *
1251  * XXX this is a horrid kluge --- it'd be better to fix the performance
1252  * problem inside tuplestore. FIXME
1253  */
1254  if (!tuplestore_in_memory(winstate->buffer))
1255  pos = -1;
1256 
1257  outerPlan = outerPlanState(winstate);
1258 
1259  /* Must be in query context to call outerplan */
1261 
1262  while (winstate->spooled_rows <= pos || pos == -1)
1263  {
1264  outerslot = ExecProcNode(outerPlan);
1265  if (TupIsNull(outerslot))
1266  {
1267  /* reached the end of the last partition */
1268  winstate->partition_spooled = true;
1269  winstate->more_partitions = false;
1270  break;
1271  }
1272 
1273  if (node->partNumCols > 0)
1274  {
1275  ExprContext *econtext = winstate->tmpcontext;
1276 
1277  econtext->ecxt_innertuple = winstate->first_part_slot;
1278  econtext->ecxt_outertuple = outerslot;
1279 
1280  /* Check if this tuple still belongs to the current partition */
1281  if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1282  {
1283  /*
1284  * end of partition; copy the tuple for the next cycle.
1285  */
1286  ExecCopySlot(winstate->first_part_slot, outerslot);
1287  winstate->partition_spooled = true;
1288  winstate->more_partitions = true;
1289  break;
1290  }
1291  }
1292 
1293  /* Still in partition, so save it into the tuplestore */
1294  tuplestore_puttupleslot(winstate->buffer, outerslot);
1295  winstate->spooled_rows++;
1296  }
1297 
1298  MemoryContextSwitchTo(oldcontext);
1299 }
1300 
1301 /*
1302  * release_partition
1303  * clear information kept within a partition, including
1304  * tuplestore and aggregate results.
1305  */
1306 static void
1308 {
1309  int i;
1310 
1311  for (i = 0; i < winstate->numfuncs; i++)
1312  {
1313  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1314 
1315  /* Release any partition-local state of this window function */
1316  if (perfuncstate->winobj)
1317  perfuncstate->winobj->localmem = NULL;
1318  }
1319 
1320  /*
1321  * Release all partition-local memory (in particular, any partition-local
1322  * state that we might have trashed our pointers to in the above loop, and
1323  * any aggregate temp data). We don't rely on retail pfree because some
1324  * aggregates might have allocated data we don't have direct pointers to.
1325  */
1328  for (i = 0; i < winstate->numaggs; i++)
1329  {
1330  if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1332  }
1333 
1334  if (winstate->buffer)
1335  tuplestore_end(winstate->buffer);
1336  winstate->buffer = NULL;
1337  winstate->partition_spooled = false;
1338 }
1339 
1340 /*
1341  * row_is_in_frame
1342  * Determine whether a row is in the current row's window frame according
1343  * to our window framing rule
1344  *
1345  * The caller must have already determined that the row is in the partition
1346  * and fetched it into a slot. This function just encapsulates the framing
1347  * rules.
1348  *
1349  * Returns:
1350  * -1, if the row is out of frame and no succeeding rows can be in frame
1351  * 0, if the row is out of frame but succeeding rows might be in frame
1352  * 1, if the row is in frame
1353  *
1354  * May clobber winstate->temp_slot_2.
1355  */
1356 static int
1358 {
1359  int frameOptions = winstate->frameOptions;
1360 
1361  Assert(pos >= 0); /* else caller error */
1362 
1363  /*
1364  * First, check frame starting conditions. We might as well delegate this
1365  * to update_frameheadpos always; it doesn't add any notable cost.
1366  */
1367  update_frameheadpos(winstate);
1368  if (pos < winstate->frameheadpos)
1369  return 0;
1370 
1371  /*
1372  * Okay so far, now check frame ending conditions. Here, we avoid calling
1373  * update_frametailpos in simple cases, so as not to spool tuples further
1374  * ahead than necessary.
1375  */
1376  if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1377  {
1378  if (frameOptions & FRAMEOPTION_ROWS)
1379  {
1380  /* rows after current row are out of frame */
1381  if (pos > winstate->currentpos)
1382  return -1;
1383  }
1384  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1385  {
1386  /* following row that is not peer is out of frame */
1387  if (pos > winstate->currentpos &&
1388  !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1389  return -1;
1390  }
1391  else
1392  Assert(false);
1393  }
1394  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1395  {
1396  if (frameOptions & FRAMEOPTION_ROWS)
1397  {
1398  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1399 
1400  /* rows after current row + offset are out of frame */
1401  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1402  offset = -offset;
1403 
1404  if (pos > winstate->currentpos + offset)
1405  return -1;
1406  }
1407  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1408  {
1409  /* hard cases, so delegate to update_frametailpos */
1410  update_frametailpos(winstate);
1411  if (pos >= winstate->frametailpos)
1412  return -1;
1413  }
1414  else
1415  Assert(false);
1416  }
1417 
1418  /* Check exclusion clause */
1419  if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1420  {
1421  if (pos == winstate->currentpos)
1422  return 0;
1423  }
1424  else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1425  ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1426  pos != winstate->currentpos))
1427  {
1428  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1429 
1430  /* If no ORDER BY, all rows are peers with each other */
1431  if (node->ordNumCols == 0)
1432  return 0;
1433  /* Otherwise, check the group boundaries */
1434  if (pos >= winstate->groupheadpos)
1435  {
1436  update_grouptailpos(winstate);
1437  if (pos < winstate->grouptailpos)
1438  return 0;
1439  }
1440  }
1441 
1442  /* If we get here, it's in frame */
1443  return 1;
1444 }
1445 
1446 /*
1447  * update_frameheadpos
1448  * make frameheadpos valid for the current row
1449  *
1450  * Note that frameheadpos is computed without regard for any window exclusion
1451  * clause; the current row and/or its peers are considered part of the frame
1452  * for this purpose even if they must be excluded later.
1453  *
1454  * May clobber winstate->temp_slot_2.
1455  */
1456 static void
1458 {
1459  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1460  int frameOptions = winstate->frameOptions;
1461  MemoryContext oldcontext;
1462 
1463  if (winstate->framehead_valid)
1464  return; /* already known for current row */
1465 
1466  /* We may be called in a short-lived context */
1468 
1469  if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1470  {
1471  /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1472  winstate->frameheadpos = 0;
1473  winstate->framehead_valid = true;
1474  }
1475  else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1476  {
1477  if (frameOptions & FRAMEOPTION_ROWS)
1478  {
1479  /* In ROWS mode, frame head is the same as current */
1480  winstate->frameheadpos = winstate->currentpos;
1481  winstate->framehead_valid = true;
1482  }
1483  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1484  {
1485  /* If no ORDER BY, all rows are peers with each other */
1486  if (node->ordNumCols == 0)
1487  {
1488  winstate->frameheadpos = 0;
1489  winstate->framehead_valid = true;
1490  MemoryContextSwitchTo(oldcontext);
1491  return;
1492  }
1493 
1494  /*
1495  * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1496  * first row that is a peer of current row. We keep a copy of the
1497  * last-known frame head row in framehead_slot, and advance as
1498  * necessary. Note that if we reach end of partition, we will
1499  * leave frameheadpos = end+1 and framehead_slot empty.
1500  */
1502  winstate->framehead_ptr);
1503  if (winstate->frameheadpos == 0 &&
1504  TupIsNull(winstate->framehead_slot))
1505  {
1506  /* fetch first row into framehead_slot, if we didn't already */
1507  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1508  winstate->framehead_slot))
1509  elog(ERROR, "unexpected end of tuplestore");
1510  }
1511 
1512  while (!TupIsNull(winstate->framehead_slot))
1513  {
1514  if (are_peers(winstate, winstate->framehead_slot,
1515  winstate->ss.ss_ScanTupleSlot))
1516  break; /* this row is the correct frame head */
1517  /* Note we advance frameheadpos even if the fetch fails */
1518  winstate->frameheadpos++;
1519  spool_tuples(winstate, winstate->frameheadpos);
1520  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1521  winstate->framehead_slot))
1522  break; /* end of partition */
1523  }
1524  winstate->framehead_valid = true;
1525  }
1526  else
1527  Assert(false);
1528  }
1529  else if (frameOptions & FRAMEOPTION_START_OFFSET)
1530  {
1531  if (frameOptions & FRAMEOPTION_ROWS)
1532  {
1533  /* In ROWS mode, bound is physically n before/after current */
1534  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1535 
1536  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1537  offset = -offset;
1538 
1539  winstate->frameheadpos = winstate->currentpos + offset;
1540  /* frame head can't go before first row */
1541  if (winstate->frameheadpos < 0)
1542  winstate->frameheadpos = 0;
1543  else if (winstate->frameheadpos > winstate->currentpos + 1)
1544  {
1545  /* make sure frameheadpos is not past end of partition */
1546  spool_tuples(winstate, winstate->frameheadpos - 1);
1547  if (winstate->frameheadpos > winstate->spooled_rows)
1548  winstate->frameheadpos = winstate->spooled_rows;
1549  }
1550  winstate->framehead_valid = true;
1551  }
1552  else if (frameOptions & FRAMEOPTION_RANGE)
1553  {
1554  /*
1555  * In RANGE START_OFFSET mode, frame head is the first row that
1556  * satisfies the in_range constraint relative to the current row.
1557  * We keep a copy of the last-known frame head row in
1558  * framehead_slot, and advance as necessary. Note that if we
1559  * reach end of partition, we will leave frameheadpos = end+1 and
1560  * framehead_slot empty.
1561  */
1562  int sortCol = node->ordColIdx[0];
1563  bool sub,
1564  less;
1565 
1566  /* Precompute flags for in_range checks */
1567  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1568  sub = true; /* subtract startOffset from current row */
1569  else
1570  sub = false; /* add it */
1571  less = false; /* normally, we want frame head >= sum */
1572  /* If sort order is descending, flip both flags */
1573  if (!winstate->inRangeAsc)
1574  {
1575  sub = !sub;
1576  less = true;
1577  }
1578 
1580  winstate->framehead_ptr);
1581  if (winstate->frameheadpos == 0 &&
1582  TupIsNull(winstate->framehead_slot))
1583  {
1584  /* fetch first row into framehead_slot, if we didn't already */
1585  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1586  winstate->framehead_slot))
1587  elog(ERROR, "unexpected end of tuplestore");
1588  }
1589 
1590  while (!TupIsNull(winstate->framehead_slot))
1591  {
1592  Datum headval,
1593  currval;
1594  bool headisnull,
1595  currisnull;
1596 
1597  headval = slot_getattr(winstate->framehead_slot, sortCol,
1598  &headisnull);
1599  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1600  &currisnull);
1601  if (headisnull || currisnull)
1602  {
1603  /* order of the rows depends only on nulls_first */
1604  if (winstate->inRangeNullsFirst)
1605  {
1606  /* advance head if head is null and curr is not */
1607  if (!headisnull || currisnull)
1608  break;
1609  }
1610  else
1611  {
1612  /* advance head if head is not null and curr is null */
1613  if (headisnull || !currisnull)
1614  break;
1615  }
1616  }
1617  else
1618  {
1620  winstate->inRangeColl,
1621  headval,
1622  currval,
1623  winstate->startOffsetValue,
1624  BoolGetDatum(sub),
1625  BoolGetDatum(less))))
1626  break; /* this row is the correct frame head */
1627  }
1628  /* Note we advance frameheadpos even if the fetch fails */
1629  winstate->frameheadpos++;
1630  spool_tuples(winstate, winstate->frameheadpos);
1631  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1632  winstate->framehead_slot))
1633  break; /* end of partition */
1634  }
1635  winstate->framehead_valid = true;
1636  }
1637  else if (frameOptions & FRAMEOPTION_GROUPS)
1638  {
1639  /*
1640  * In GROUPS START_OFFSET mode, frame head is the first row of the
1641  * first peer group whose number satisfies the offset constraint.
1642  * We keep a copy of the last-known frame head row in
1643  * framehead_slot, and advance as necessary. Note that if we
1644  * reach end of partition, we will leave frameheadpos = end+1 and
1645  * framehead_slot empty.
1646  */
1647  int64 offset = DatumGetInt64(winstate->startOffsetValue);
1648  int64 minheadgroup;
1649 
1650  if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1651  minheadgroup = winstate->currentgroup - offset;
1652  else
1653  minheadgroup = winstate->currentgroup + offset;
1654 
1656  winstate->framehead_ptr);
1657  if (winstate->frameheadpos == 0 &&
1658  TupIsNull(winstate->framehead_slot))
1659  {
1660  /* fetch first row into framehead_slot, if we didn't already */
1661  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1662  winstate->framehead_slot))
1663  elog(ERROR, "unexpected end of tuplestore");
1664  }
1665 
1666  while (!TupIsNull(winstate->framehead_slot))
1667  {
1668  if (winstate->frameheadgroup >= minheadgroup)
1669  break; /* this row is the correct frame head */
1670  ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1671  /* Note we advance frameheadpos even if the fetch fails */
1672  winstate->frameheadpos++;
1673  spool_tuples(winstate, winstate->frameheadpos);
1674  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1675  winstate->framehead_slot))
1676  break; /* end of partition */
1677  if (!are_peers(winstate, winstate->temp_slot_2,
1678  winstate->framehead_slot))
1679  winstate->frameheadgroup++;
1680  }
1681  ExecClearTuple(winstate->temp_slot_2);
1682  winstate->framehead_valid = true;
1683  }
1684  else
1685  Assert(false);
1686  }
1687  else
1688  Assert(false);
1689 
1690  MemoryContextSwitchTo(oldcontext);
1691 }
1692 
1693 /*
1694  * update_frametailpos
1695  * make frametailpos valid for the current row
1696  *
1697  * Note that frametailpos is computed without regard for any window exclusion
1698  * clause; the current row and/or its peers are considered part of the frame
1699  * for this purpose even if they must be excluded later.
1700  *
1701  * May clobber winstate->temp_slot_2.
1702  */
1703 static void
1705 {
1706  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1707  int frameOptions = winstate->frameOptions;
1708  MemoryContext oldcontext;
1709 
1710  if (winstate->frametail_valid)
1711  return; /* already known for current row */
1712 
1713  /* We may be called in a short-lived context */
1715 
1716  if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1717  {
1718  /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1719  spool_tuples(winstate, -1);
1720  winstate->frametailpos = winstate->spooled_rows;
1721  winstate->frametail_valid = true;
1722  }
1723  else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1724  {
1725  if (frameOptions & FRAMEOPTION_ROWS)
1726  {
1727  /* In ROWS mode, exactly the rows up to current are in frame */
1728  winstate->frametailpos = winstate->currentpos + 1;
1729  winstate->frametail_valid = true;
1730  }
1731  else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1732  {
1733  /* If no ORDER BY, all rows are peers with each other */
1734  if (node->ordNumCols == 0)
1735  {
1736  spool_tuples(winstate, -1);
1737  winstate->frametailpos = winstate->spooled_rows;
1738  winstate->frametail_valid = true;
1739  MemoryContextSwitchTo(oldcontext);
1740  return;
1741  }
1742 
1743  /*
1744  * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1745  * row that is a peer of current row, frame tail is the row after
1746  * that (if any). We keep a copy of the last-known frame tail row
1747  * in frametail_slot, and advance as necessary. Note that if we
1748  * reach end of partition, we will leave frametailpos = end+1 and
1749  * frametail_slot empty.
1750  */
1752  winstate->frametail_ptr);
1753  if (winstate->frametailpos == 0 &&
1754  TupIsNull(winstate->frametail_slot))
1755  {
1756  /* fetch first row into frametail_slot, if we didn't already */
1757  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1758  winstate->frametail_slot))
1759  elog(ERROR, "unexpected end of tuplestore");
1760  }
1761 
1762  while (!TupIsNull(winstate->frametail_slot))
1763  {
1764  if (winstate->frametailpos > winstate->currentpos &&
1765  !are_peers(winstate, winstate->frametail_slot,
1766  winstate->ss.ss_ScanTupleSlot))
1767  break; /* this row is the frame tail */
1768  /* Note we advance frametailpos even if the fetch fails */
1769  winstate->frametailpos++;
1770  spool_tuples(winstate, winstate->frametailpos);
1771  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1772  winstate->frametail_slot))
1773  break; /* end of partition */
1774  }
1775  winstate->frametail_valid = true;
1776  }
1777  else
1778  Assert(false);
1779  }
1780  else if (frameOptions & FRAMEOPTION_END_OFFSET)
1781  {
1782  if (frameOptions & FRAMEOPTION_ROWS)
1783  {
1784  /* In ROWS mode, bound is physically n before/after current */
1785  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1786 
1787  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1788  offset = -offset;
1789 
1790  winstate->frametailpos = winstate->currentpos + offset + 1;
1791  /* smallest allowable value of frametailpos is 0 */
1792  if (winstate->frametailpos < 0)
1793  winstate->frametailpos = 0;
1794  else if (winstate->frametailpos > winstate->currentpos + 1)
1795  {
1796  /* make sure frametailpos is not past end of partition */
1797  spool_tuples(winstate, winstate->frametailpos - 1);
1798  if (winstate->frametailpos > winstate->spooled_rows)
1799  winstate->frametailpos = winstate->spooled_rows;
1800  }
1801  winstate->frametail_valid = true;
1802  }
1803  else if (frameOptions & FRAMEOPTION_RANGE)
1804  {
1805  /*
1806  * In RANGE END_OFFSET mode, frame end is the last row that
1807  * satisfies the in_range constraint relative to the current row,
1808  * frame tail is the row after that (if any). We keep a copy of
1809  * the last-known frame tail row in frametail_slot, and advance as
1810  * necessary. Note that if we reach end of partition, we will
1811  * leave frametailpos = end+1 and frametail_slot empty.
1812  */
1813  int sortCol = node->ordColIdx[0];
1814  bool sub,
1815  less;
1816 
1817  /* Precompute flags for in_range checks */
1818  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1819  sub = true; /* subtract endOffset from current row */
1820  else
1821  sub = false; /* add it */
1822  less = true; /* normally, we want frame tail <= sum */
1823  /* If sort order is descending, flip both flags */
1824  if (!winstate->inRangeAsc)
1825  {
1826  sub = !sub;
1827  less = false;
1828  }
1829 
1831  winstate->frametail_ptr);
1832  if (winstate->frametailpos == 0 &&
1833  TupIsNull(winstate->frametail_slot))
1834  {
1835  /* fetch first row into frametail_slot, if we didn't already */
1836  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1837  winstate->frametail_slot))
1838  elog(ERROR, "unexpected end of tuplestore");
1839  }
1840 
1841  while (!TupIsNull(winstate->frametail_slot))
1842  {
1843  Datum tailval,
1844  currval;
1845  bool tailisnull,
1846  currisnull;
1847 
1848  tailval = slot_getattr(winstate->frametail_slot, sortCol,
1849  &tailisnull);
1850  currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1851  &currisnull);
1852  if (tailisnull || currisnull)
1853  {
1854  /* order of the rows depends only on nulls_first */
1855  if (winstate->inRangeNullsFirst)
1856  {
1857  /* advance tail if tail is null or curr is not */
1858  if (!tailisnull)
1859  break;
1860  }
1861  else
1862  {
1863  /* advance tail if tail is not null or curr is null */
1864  if (!currisnull)
1865  break;
1866  }
1867  }
1868  else
1869  {
1871  winstate->inRangeColl,
1872  tailval,
1873  currval,
1874  winstate->endOffsetValue,
1875  BoolGetDatum(sub),
1876  BoolGetDatum(less))))
1877  break; /* this row is the correct frame tail */
1878  }
1879  /* Note we advance frametailpos even if the fetch fails */
1880  winstate->frametailpos++;
1881  spool_tuples(winstate, winstate->frametailpos);
1882  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1883  winstate->frametail_slot))
1884  break; /* end of partition */
1885  }
1886  winstate->frametail_valid = true;
1887  }
1888  else if (frameOptions & FRAMEOPTION_GROUPS)
1889  {
1890  /*
1891  * In GROUPS END_OFFSET mode, frame end is the last row of the
1892  * last peer group whose number satisfies the offset constraint,
1893  * and frame tail is the row after that (if any). We keep a copy
1894  * of the last-known frame tail row in frametail_slot, and advance
1895  * as necessary. Note that if we reach end of partition, we will
1896  * leave frametailpos = end+1 and frametail_slot empty.
1897  */
1898  int64 offset = DatumGetInt64(winstate->endOffsetValue);
1899  int64 maxtailgroup;
1900 
1901  if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1902  maxtailgroup = winstate->currentgroup - offset;
1903  else
1904  maxtailgroup = winstate->currentgroup + offset;
1905 
1907  winstate->frametail_ptr);
1908  if (winstate->frametailpos == 0 &&
1909  TupIsNull(winstate->frametail_slot))
1910  {
1911  /* fetch first row into frametail_slot, if we didn't already */
1912  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1913  winstate->frametail_slot))
1914  elog(ERROR, "unexpected end of tuplestore");
1915  }
1916 
1917  while (!TupIsNull(winstate->frametail_slot))
1918  {
1919  if (winstate->frametailgroup > maxtailgroup)
1920  break; /* this row is the correct frame tail */
1921  ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
1922  /* Note we advance frametailpos even if the fetch fails */
1923  winstate->frametailpos++;
1924  spool_tuples(winstate, winstate->frametailpos);
1925  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1926  winstate->frametail_slot))
1927  break; /* end of partition */
1928  if (!are_peers(winstate, winstate->temp_slot_2,
1929  winstate->frametail_slot))
1930  winstate->frametailgroup++;
1931  }
1932  ExecClearTuple(winstate->temp_slot_2);
1933  winstate->frametail_valid = true;
1934  }
1935  else
1936  Assert(false);
1937  }
1938  else
1939  Assert(false);
1940 
1941  MemoryContextSwitchTo(oldcontext);
1942 }
1943 
1944 /*
1945  * update_grouptailpos
1946  * make grouptailpos valid for the current row
1947  *
1948  * May clobber winstate->temp_slot_2.
1949  */
1950 static void
1952 {
1953  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1954  MemoryContext oldcontext;
1955 
1956  if (winstate->grouptail_valid)
1957  return; /* already known for current row */
1958 
1959  /* We may be called in a short-lived context */
1961 
1962  /* If no ORDER BY, all rows are peers with each other */
1963  if (node->ordNumCols == 0)
1964  {
1965  spool_tuples(winstate, -1);
1966  winstate->grouptailpos = winstate->spooled_rows;
1967  winstate->grouptail_valid = true;
1968  MemoryContextSwitchTo(oldcontext);
1969  return;
1970  }
1971 
1972  /*
1973  * Because grouptail_valid is reset only when current row advances into a
1974  * new peer group, we always reach here knowing that grouptailpos needs to
1975  * be advanced by at least one row. Hence, unlike the otherwise similar
1976  * case for frame tail tracking, we do not need persistent storage of the
1977  * group tail row.
1978  */
1979  Assert(winstate->grouptailpos <= winstate->currentpos);
1981  winstate->grouptail_ptr);
1982  for (;;)
1983  {
1984  /* Note we advance grouptailpos even if the fetch fails */
1985  winstate->grouptailpos++;
1986  spool_tuples(winstate, winstate->grouptailpos);
1987  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1988  winstate->temp_slot_2))
1989  break; /* end of partition */
1990  if (winstate->grouptailpos > winstate->currentpos &&
1991  !are_peers(winstate, winstate->temp_slot_2,
1992  winstate->ss.ss_ScanTupleSlot))
1993  break; /* this row is the group tail */
1994  }
1995  ExecClearTuple(winstate->temp_slot_2);
1996  winstate->grouptail_valid = true;
1997 
1998  MemoryContextSwitchTo(oldcontext);
1999 }
2000 
2001 
2002 /* -----------------
2003  * ExecWindowAgg
2004  *
2005  * ExecWindowAgg receives tuples from its outer subplan and
2006  * stores them into a tuplestore, then processes window functions.
2007  * This node doesn't reduce nor qualify any row so the number of
2008  * returned rows is exactly the same as its outer subplan's result.
2009  * -----------------
2010  */
2011 static TupleTableSlot *
2013 {
2015  ExprContext *econtext;
2016  int i;
2017  int numfuncs;
2018 
2020 
2021  if (winstate->all_done)
2022  return NULL;
2023 
2024  /*
2025  * Compute frame offset values, if any, during first call (or after a
2026  * rescan). These are assumed to hold constant throughout the scan; if
2027  * user gives us a volatile expression, we'll only use its initial value.
2028  */
2029  if (winstate->all_first)
2030  {
2031  int frameOptions = winstate->frameOptions;
2032  ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
2033  Datum value;
2034  bool isnull;
2035  int16 len;
2036  bool byval;
2037 
2038  if (frameOptions & FRAMEOPTION_START_OFFSET)
2039  {
2040  Assert(winstate->startOffset != NULL);
2041  value = ExecEvalExprSwitchContext(winstate->startOffset,
2042  econtext,
2043  &isnull);
2044  if (isnull)
2045  ereport(ERROR,
2046  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2047  errmsg("frame starting offset must not be null")));
2048  /* copy value into query-lifespan context */
2049  get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
2050  &len, &byval);
2051  winstate->startOffsetValue = datumCopy(value, byval, len);
2052  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2053  {
2054  /* value is known to be int8 */
2055  int64 offset = DatumGetInt64(value);
2056 
2057  if (offset < 0)
2058  ereport(ERROR,
2059  (errcode(ERRCODE_INVALID_PRECEDING_FOLLOWING_SIZE),
2060  errmsg("frame starting offset must not be negative")));
2061  }
2062  }
2063  if (frameOptions & FRAMEOPTION_END_OFFSET)
2064  {
2065  Assert(winstate->endOffset != NULL);
2066  value = ExecEvalExprSwitchContext(winstate->endOffset,
2067  econtext,
2068  &isnull);
2069  if (isnull)
2070  ereport(ERROR,
2071  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2072  errmsg("frame ending offset must not be null")));
2073  /* copy value into query-lifespan context */
2074  get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2075  &len, &byval);
2076  winstate->endOffsetValue = datumCopy(value, byval, len);
2077  if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2078  {
2079  /* value is known to be int8 */
2080  int64 offset = DatumGetInt64(value);
2081 
2082  if (offset < 0)
2083  ereport(ERROR,
2084  (errcode(ERRCODE_INVALID_PRECEDING_FOLLOWING_SIZE),
2085  errmsg("frame ending offset must not be negative")));
2086  }
2087  }
2088  winstate->all_first = false;
2089  }
2090 
2091  if (winstate->buffer == NULL)
2092  {
2093  /* Initialize for first partition and set current row = 0 */
2094  begin_partition(winstate);
2095  /* If there are no input rows, we'll detect that and exit below */
2096  }
2097  else
2098  {
2099  /* Advance current row within partition */
2100  winstate->currentpos++;
2101  /* This might mean that the frame moves, too */
2102  winstate->framehead_valid = false;
2103  winstate->frametail_valid = false;
2104  /* we don't need to invalidate grouptail here; see below */
2105  }
2106 
2107  /*
2108  * Spool all tuples up to and including the current row, if we haven't
2109  * already
2110  */
2111  spool_tuples(winstate, winstate->currentpos);
2112 
2113  /* Move to the next partition if we reached the end of this partition */
2114  if (winstate->partition_spooled &&
2115  winstate->currentpos >= winstate->spooled_rows)
2116  {
2117  release_partition(winstate);
2118 
2119  if (winstate->more_partitions)
2120  {
2121  begin_partition(winstate);
2122  Assert(winstate->spooled_rows > 0);
2123  }
2124  else
2125  {
2126  winstate->all_done = true;
2127  return NULL;
2128  }
2129  }
2130 
2131  /* final output execution is in ps_ExprContext */
2132  econtext = winstate->ss.ps.ps_ExprContext;
2133 
2134  /* Clear the per-output-tuple context for current row */
2135  ResetExprContext(econtext);
2136 
2137  /*
2138  * Read the current row from the tuplestore, and save in ScanTupleSlot.
2139  * (We can't rely on the outerplan's output slot because we may have to
2140  * read beyond the current row. Also, we have to actually copy the row
2141  * out of the tuplestore, since window function evaluation might cause the
2142  * tuplestore to dump its state to disk.)
2143  *
2144  * In GROUPS mode, or when tracking a group-oriented exclusion clause, we
2145  * must also detect entering a new peer group and update associated state
2146  * when that happens. We use temp_slot_2 to temporarily hold the previous
2147  * row for this purpose.
2148  *
2149  * Current row must be in the tuplestore, since we spooled it above.
2150  */
2151  tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
2152  if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2155  winstate->currentpos > 0)
2156  {
2157  ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2158  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2159  winstate->ss.ss_ScanTupleSlot))
2160  elog(ERROR, "unexpected end of tuplestore");
2161  if (!are_peers(winstate, winstate->temp_slot_2,
2162  winstate->ss.ss_ScanTupleSlot))
2163  {
2164  winstate->currentgroup++;
2165  winstate->groupheadpos = winstate->currentpos;
2166  winstate->grouptail_valid = false;
2167  }
2168  ExecClearTuple(winstate->temp_slot_2);
2169  }
2170  else
2171  {
2172  if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2173  winstate->ss.ss_ScanTupleSlot))
2174  elog(ERROR, "unexpected end of tuplestore");
2175  }
2176 
2177  /*
2178  * Evaluate true window functions
2179  */
2180  numfuncs = winstate->numfuncs;
2181  for (i = 0; i < numfuncs; i++)
2182  {
2183  WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2184 
2185  if (perfuncstate->plain_agg)
2186  continue;
2187  eval_windowfunction(winstate, perfuncstate,
2188  &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2189  &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2190  }
2191 
2192  /*
2193  * Evaluate aggregates
2194  */
2195  if (winstate->numaggs > 0)
2196  eval_windowaggregates(winstate);
2197 
2198  /*
2199  * If we have created auxiliary read pointers for the frame or group
2200  * boundaries, force them to be kept up-to-date, because we don't know
2201  * whether the window function(s) will do anything that requires that.
2202  * Failing to advance the pointers would result in being unable to trim
2203  * data from the tuplestore, which is bad. (If we could know in advance
2204  * whether the window functions will use frame boundary info, we could
2205  * skip creating these pointers in the first place ... but unfortunately
2206  * the window function API doesn't require that.)
2207  */
2208  if (winstate->framehead_ptr >= 0)
2209  update_frameheadpos(winstate);
2210  if (winstate->frametail_ptr >= 0)
2211  update_frametailpos(winstate);
2212  if (winstate->grouptail_ptr >= 0)
2213  update_grouptailpos(winstate);
2214 
2215  /*
2216  * Truncate any no-longer-needed rows from the tuplestore.
2217  */
2218  tuplestore_trim(winstate->buffer);
2219 
2220  /*
2221  * Form and return a projection tuple using the windowfunc results and the
2222  * current row. Setting ecxt_outertuple arranges that any Vars will be
2223  * evaluated with respect to that row.
2224  */
2225  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2226 
2227  return ExecProject(winstate->ss.ps.ps_ProjInfo);
2228 }
2229 
2230 /* -----------------
2231  * ExecInitWindowAgg
2232  *
2233  * Creates the run-time information for the WindowAgg node produced by the
2234  * planner and initializes its outer subtree
2235  * -----------------
2236  */
2238 ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
2239 {
2241  Plan *outerPlan;
2242  ExprContext *econtext;
2243  ExprContext *tmpcontext;
2244  WindowStatePerFunc perfunc;
2245  WindowStatePerAgg peragg;
2246  int frameOptions = node->frameOptions;
2247  int numfuncs,
2248  wfuncno,
2249  numaggs,
2250  aggno;
2251  TupleDesc scanDesc;
2252  ListCell *l;
2253 
2254  /* check for unsupported flags */
2255  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2256 
2257  /*
2258  * create state structure
2259  */
2260  winstate = makeNode(WindowAggState);
2261  winstate->ss.ps.plan = (Plan *) node;
2262  winstate->ss.ps.state = estate;
2263  winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2264 
2265  /*
2266  * Create expression contexts. We need two, one for per-input-tuple
2267  * processing and one for per-output-tuple processing. We cheat a little
2268  * by using ExecAssignExprContext() to build both.
2269  */
2270  ExecAssignExprContext(estate, &winstate->ss.ps);
2271  tmpcontext = winstate->ss.ps.ps_ExprContext;
2272  winstate->tmpcontext = tmpcontext;
2273  ExecAssignExprContext(estate, &winstate->ss.ps);
2274 
2275  /* Create long-lived context for storage of partition-local memory etc */
2276  winstate->partcontext =
2278  "WindowAgg Partition",
2280 
2281  /*
2282  * Create mid-lived context for aggregate trans values etc.
2283  *
2284  * Note that moving aggregates each use their own private context, not
2285  * this one.
2286  */
2287  winstate->aggcontext =
2289  "WindowAgg Aggregates",
2291 
2292  /*
2293  * WindowAgg nodes never have quals, since they can only occur at the
2294  * logical top level of a query (ie, after any WHERE or HAVING filters)
2295  */
2296  Assert(node->plan.qual == NIL);
2297  winstate->ss.ps.qual = NULL;
2298 
2299  /*
2300  * initialize child nodes
2301  */
2302  outerPlan = outerPlan(node);
2303  outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2304 
2305  /*
2306  * initialize source tuple type (which is also the tuple type that we'll
2307  * store in the tuplestore and use in all our working slots).
2308  */
2309  ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss);
2310  scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2311 
2312  /*
2313  * tuple table initialization
2314  */
2315  winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2316  winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2317  winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc);
2318  winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc);
2319 
2320  /*
2321  * create frame head and tail slots only if needed (must match logic in
2322  * update_frameheadpos and update_frametailpos)
2323  */
2324  winstate->framehead_slot = winstate->frametail_slot = NULL;
2325 
2326  if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2327  {
2328  if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING))
2329  winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2330  if (!(frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING))
2331  winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc);
2332  }
2333 
2334  /*
2335  * Initialize result slot, type and projection.
2336  */
2337  ExecInitResultTupleSlotTL(estate, &winstate->ss.ps);
2338  ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2339 
2340  /* Set up data for comparing tuples */
2341  if (node->partNumCols > 0)
2342  winstate->partEqfunction =
2343  execTuplesMatchPrepare(scanDesc,
2344  node->partNumCols,
2345  node->partColIdx,
2346  node->partOperators,
2347  &winstate->ss.ps);
2348 
2349  if (node->ordNumCols > 0)
2350  winstate->ordEqfunction =
2351  execTuplesMatchPrepare(scanDesc,
2352  node->ordNumCols,
2353  node->ordColIdx,
2354  node->ordOperators,
2355  &winstate->ss.ps);
2356 
2357  /*
2358  * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2359  */
2360  numfuncs = winstate->numfuncs;
2361  numaggs = winstate->numaggs;
2362  econtext = winstate->ss.ps.ps_ExprContext;
2363  econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2364  econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2365 
2366  /*
2367  * allocate per-wfunc/per-agg state information.
2368  */
2369  perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2370  peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2371  winstate->perfunc = perfunc;
2372  winstate->peragg = peragg;
2373 
2374  wfuncno = -1;
2375  aggno = -1;
2376  foreach(l, winstate->funcs)
2377  {
2378  WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2379  WindowFunc *wfunc = wfuncstate->wfunc;
2380  WindowStatePerFunc perfuncstate;
2381  AclResult aclresult;
2382  int i;
2383 
2384  if (wfunc->winref != node->winref) /* planner screwed up? */
2385  elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2386  wfunc->winref, node->winref);
2387 
2388  /* Look for a previous duplicate window function */
2389  for (i = 0; i <= wfuncno; i++)
2390  {
2391  if (equal(wfunc, perfunc[i].wfunc) &&
2392  !contain_volatile_functions((Node *) wfunc))
2393  break;
2394  }
2395  if (i <= wfuncno)
2396  {
2397  /* Found a match to an existing entry, so just mark it */
2398  wfuncstate->wfuncno = i;
2399  continue;
2400  }
2401 
2402  /* Nope, so assign a new PerAgg record */
2403  perfuncstate = &perfunc[++wfuncno];
2404 
2405  /* Mark WindowFunc state node with assigned index in the result array */
2406  wfuncstate->wfuncno = wfuncno;
2407 
2408  /* Check permission to call window function */
2409  aclresult = pg_proc_aclcheck(wfunc->winfnoid, GetUserId(),
2410  ACL_EXECUTE);
2411  if (aclresult != ACLCHECK_OK)
2412  aclcheck_error(aclresult, OBJECT_FUNCTION,
2413  get_func_name(wfunc->winfnoid));
2415 
2416  /* Fill in the perfuncstate data */
2417  perfuncstate->wfuncstate = wfuncstate;
2418  perfuncstate->wfunc = wfunc;
2419  perfuncstate->numArguments = list_length(wfuncstate->args);
2420 
2421  fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2422  econtext->ecxt_per_query_memory);
2423  fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2424 
2425  perfuncstate->winCollation = wfunc->inputcollid;
2426 
2427  get_typlenbyval(wfunc->wintype,
2428  &perfuncstate->resulttypeLen,
2429  &perfuncstate->resulttypeByVal);
2430 
2431  /*
2432  * If it's really just a plain aggregate function, we'll emulate the
2433  * Agg environment for it.
2434  */
2435  perfuncstate->plain_agg = wfunc->winagg;
2436  if (wfunc->winagg)
2437  {
2438  WindowStatePerAgg peraggstate;
2439 
2440  perfuncstate->aggno = ++aggno;
2441  peraggstate = &winstate->peragg[aggno];
2442  initialize_peragg(winstate, wfunc, peraggstate);
2443  peraggstate->wfuncno = wfuncno;
2444  }
2445  else
2446  {
2448 
2449  winobj->winstate = winstate;
2450  winobj->argstates = wfuncstate->args;
2451  winobj->localmem = NULL;
2452  perfuncstate->winobj = winobj;
2453  }
2454  }
2455 
2456  /* Update numfuncs, numaggs to match number of unique functions found */
2457  winstate->numfuncs = wfuncno + 1;
2458  winstate->numaggs = aggno + 1;
2459 
2460  /* Set up WindowObject for aggregates, if needed */
2461  if (winstate->numaggs > 0)
2462  {
2463  WindowObject agg_winobj = makeNode(WindowObjectData);
2464 
2465  agg_winobj->winstate = winstate;
2466  agg_winobj->argstates = NIL;
2467  agg_winobj->localmem = NULL;
2468  /* make sure markptr = -1 to invalidate. It may not get used */
2469  agg_winobj->markptr = -1;
2470  agg_winobj->readptr = -1;
2471  winstate->agg_winobj = agg_winobj;
2472  }
2473 
2474  /* copy frame options to state node for easy access */
2475  winstate->frameOptions = frameOptions;
2476 
2477  /* initialize frame bound offset expressions */
2478  winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2479  (PlanState *) winstate);
2480  winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2481  (PlanState *) winstate);
2482 
2483  /* Lookup in_range support functions if needed */
2484  if (OidIsValid(node->startInRangeFunc))
2485  fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2486  if (OidIsValid(node->endInRangeFunc))
2487  fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2488  winstate->inRangeColl = node->inRangeColl;
2489  winstate->inRangeAsc = node->inRangeAsc;
2490  winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2491 
2492  winstate->all_first = true;
2493  winstate->partition_spooled = false;
2494  winstate->more_partitions = false;
2495 
2496  return winstate;
2497 }
2498 
2499 /* -----------------
2500  * ExecEndWindowAgg
2501  * -----------------
2502  */
2503 void
2505 {
2507  int i;
2508 
2509  release_partition(node);
2510 
2514  ExecClearTuple(node->temp_slot_1);
2515  ExecClearTuple(node->temp_slot_2);
2516  if (node->framehead_slot)
2518  if (node->frametail_slot)
2520 
2521  /*
2522  * Free both the expr contexts.
2523  */
2524  ExecFreeExprContext(&node->ss.ps);
2525  node->ss.ps.ps_ExprContext = node->tmpcontext;
2526  ExecFreeExprContext(&node->ss.ps);
2527 
2528  for (i = 0; i < node->numaggs; i++)
2529  {
2530  if (node->peragg[i].aggcontext != node->aggcontext)
2532  }
2535 
2536  pfree(node->perfunc);
2537  pfree(node->peragg);
2538 
2539  outerPlan = outerPlanState(node);
2540  ExecEndNode(outerPlan);
2541 }
2542 
2543 /* -----------------
2544  * ExecReScanWindowAgg
2545  * -----------------
2546  */
2547 void
2549 {
2551  ExprContext *econtext = node->ss.ps.ps_ExprContext;
2552 
2553  node->all_done = false;
2554  node->all_first = true;
2555 
2556  /* release tuplestore et al */
2557  release_partition(node);
2558 
2559  /* release all temp tuples, but especially first_part_slot */
2563  ExecClearTuple(node->temp_slot_1);
2564  ExecClearTuple(node->temp_slot_2);
2565  if (node->framehead_slot)
2567  if (node->frametail_slot)
2569 
2570  /* Forget current wfunc values */
2571  MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2572  MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2573 
2574  /*
2575  * if chgParam of subnode is not null then plan will be re-scanned by
2576  * first ExecProcNode.
2577  */
2578  if (outerPlan->chgParam == NULL)
2579  ExecReScan(outerPlan);
2580 }
2581 
2582 /*
2583  * initialize_peragg
2584  *
2585  * Almost same as in nodeAgg.c, except we don't support DISTINCT currently.
2586  */
2587 static WindowStatePerAggData *
2589  WindowStatePerAgg peraggstate)
2590 {
2591  Oid inputTypes[FUNC_MAX_ARGS];
2592  int numArguments;
2593  HeapTuple aggTuple;
2594  Form_pg_aggregate aggform;
2595  Oid aggtranstype;
2596  AttrNumber initvalAttNo;
2597  AclResult aclresult;
2598  bool use_ma_code;
2599  Oid transfn_oid,
2600  invtransfn_oid,
2601  finalfn_oid;
2602  bool finalextra;
2603  char finalmodify;
2604  Expr *transfnexpr,
2605  *invtransfnexpr,
2606  *finalfnexpr;
2607  Datum textInitVal;
2608  int i;
2609  ListCell *lc;
2610 
2611  numArguments = list_length(wfunc->args);
2612 
2613  i = 0;
2614  foreach(lc, wfunc->args)
2615  {
2616  inputTypes[i++] = exprType((Node *) lfirst(lc));
2617  }
2618 
2619  aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
2620  if (!HeapTupleIsValid(aggTuple))
2621  elog(ERROR, "cache lookup failed for aggregate %u",
2622  wfunc->winfnoid);
2623  aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
2624 
2625  /*
2626  * Figure out whether we want to use the moving-aggregate implementation,
2627  * and collect the right set of fields from the pg_attribute entry.
2628  *
2629  * It's possible that an aggregate would supply a safe moving-aggregate
2630  * implementation and an unsafe normal one, in which case our hand is
2631  * forced. Otherwise, if the frame head can't move, we don't need
2632  * moving-aggregate code. Even if we'd like to use it, don't do so if the
2633  * aggregate's arguments (and FILTER clause if any) contain any calls to
2634  * volatile functions. Otherwise, the difference between restarting and
2635  * not restarting the aggregation would be user-visible.
2636  */
2637  if (!OidIsValid(aggform->aggminvtransfn))
2638  use_ma_code = false; /* sine qua non */
2639  else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2640  aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2641  use_ma_code = true; /* decision forced by safety */
2643  use_ma_code = false; /* non-moving frame head */
2644  else if (contain_volatile_functions((Node *) wfunc))
2645  use_ma_code = false; /* avoid possible behavioral change */
2646  else
2647  use_ma_code = true; /* yes, let's use it */
2648  if (use_ma_code)
2649  {
2650  peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2651  peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2652  peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2653  finalextra = aggform->aggmfinalextra;
2654  finalmodify = aggform->aggmfinalmodify;
2655  aggtranstype = aggform->aggmtranstype;
2656  initvalAttNo = Anum_pg_aggregate_aggminitval;
2657  }
2658  else
2659  {
2660  peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2661  peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2662  peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2663  finalextra = aggform->aggfinalextra;
2664  finalmodify = aggform->aggfinalmodify;
2665  aggtranstype = aggform->aggtranstype;
2666  initvalAttNo = Anum_pg_aggregate_agginitval;
2667  }
2668 
2669  /*
2670  * ExecInitWindowAgg already checked permission to call aggregate function
2671  * ... but we still need to check the component functions
2672  */
2673 
2674  /* Check that aggregate owner has permission to call component fns */
2675  {
2676  HeapTuple procTuple;
2677  Oid aggOwner;
2678 
2679  procTuple = SearchSysCache1(PROCOID,
2680  ObjectIdGetDatum(wfunc->winfnoid));
2681  if (!HeapTupleIsValid(procTuple))
2682  elog(ERROR, "cache lookup failed for function %u",
2683  wfunc->winfnoid);
2684  aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
2685  ReleaseSysCache(procTuple);
2686 
2687  aclresult = pg_proc_aclcheck(transfn_oid, aggOwner,
2688  ACL_EXECUTE);
2689  if (aclresult != ACLCHECK_OK)
2690  aclcheck_error(aclresult, OBJECT_FUNCTION,
2691  get_func_name(transfn_oid));
2692  InvokeFunctionExecuteHook(transfn_oid);
2693 
2694  if (OidIsValid(invtransfn_oid))
2695  {
2696  aclresult = pg_proc_aclcheck(invtransfn_oid, aggOwner,
2697  ACL_EXECUTE);
2698  if (aclresult != ACLCHECK_OK)
2699  aclcheck_error(aclresult, OBJECT_FUNCTION,
2700  get_func_name(invtransfn_oid));
2701  InvokeFunctionExecuteHook(invtransfn_oid);
2702  }
2703 
2704  if (OidIsValid(finalfn_oid))
2705  {
2706  aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
2707  ACL_EXECUTE);
2708  if (aclresult != ACLCHECK_OK)
2709  aclcheck_error(aclresult, OBJECT_FUNCTION,
2710  get_func_name(finalfn_oid));
2711  InvokeFunctionExecuteHook(finalfn_oid);
2712  }
2713  }
2714 
2715  /*
2716  * If the selected finalfn isn't read-only, we can't run this aggregate as
2717  * a window function. This is a user-facing error, so we take a bit more
2718  * care with the error message than elsewhere in this function.
2719  */
2720  if (finalmodify != AGGMODIFY_READ_ONLY)
2721  ereport(ERROR,
2722  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2723  errmsg("aggregate function %s does not support use as a window function",
2724  format_procedure(wfunc->winfnoid))));
2725 
2726  /* Detect how many arguments to pass to the finalfn */
2727  if (finalextra)
2728  peraggstate->numFinalArgs = numArguments + 1;
2729  else
2730  peraggstate->numFinalArgs = 1;
2731 
2732  /* resolve actual type of transition state, if polymorphic */
2733  aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
2734  aggtranstype,
2735  inputTypes,
2736  numArguments);
2737 
2738  /* build expression trees using actual argument & result types */
2739  build_aggregate_transfn_expr(inputTypes,
2740  numArguments,
2741  0, /* no ordered-set window functions yet */
2742  false, /* no variadic window functions yet */
2743  aggtranstype,
2744  wfunc->inputcollid,
2745  transfn_oid,
2746  invtransfn_oid,
2747  &transfnexpr,
2748  &invtransfnexpr);
2749 
2750  /* set up infrastructure for calling the transfn(s) and finalfn */
2751  fmgr_info(transfn_oid, &peraggstate->transfn);
2752  fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
2753 
2754  if (OidIsValid(invtransfn_oid))
2755  {
2756  fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
2757  fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
2758  }
2759 
2760  if (OidIsValid(finalfn_oid))
2761  {
2762  build_aggregate_finalfn_expr(inputTypes,
2763  peraggstate->numFinalArgs,
2764  aggtranstype,
2765  wfunc->wintype,
2766  wfunc->inputcollid,
2767  finalfn_oid,
2768  &finalfnexpr);
2769  fmgr_info(finalfn_oid, &peraggstate->finalfn);
2770  fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
2771  }
2772 
2773  /* get info about relevant datatypes */
2774  get_typlenbyval(wfunc->wintype,
2775  &peraggstate->resulttypeLen,
2776  &peraggstate->resulttypeByVal);
2777  get_typlenbyval(aggtranstype,
2778  &peraggstate->transtypeLen,
2779  &peraggstate->transtypeByVal);
2780 
2781  /*
2782  * initval is potentially null, so don't try to access it as a struct
2783  * field. Must do it the hard way with SysCacheGetAttr.
2784  */
2785  textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
2786  &peraggstate->initValueIsNull);
2787 
2788  if (peraggstate->initValueIsNull)
2789  peraggstate->initValue = (Datum) 0;
2790  else
2791  peraggstate->initValue = GetAggInitVal(textInitVal,
2792  aggtranstype);
2793 
2794  /*
2795  * If the transfn is strict and the initval is NULL, make sure input type
2796  * and transtype are the same (or at least binary-compatible), so that
2797  * it's OK to use the first input value as the initial transValue. This
2798  * should have been checked at agg definition time, but we must check
2799  * again in case the transfn's strictness property has been changed.
2800  */
2801  if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
2802  {
2803  if (numArguments < 1 ||
2804  !IsBinaryCoercible(inputTypes[0], aggtranstype))
2805  ereport(ERROR,
2806  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2807  errmsg("aggregate %u needs to have compatible input type and transition type",
2808  wfunc->winfnoid)));
2809  }
2810 
2811  /*
2812  * Insist that forward and inverse transition functions have the same
2813  * strictness setting. Allowing them to differ would require handling
2814  * more special cases in advance_windowaggregate and
2815  * advance_windowaggregate_base, for no discernible benefit. This should
2816  * have been checked at agg definition time, but we must check again in
2817  * case either function's strictness property has been changed.
2818  */
2819  if (OidIsValid(invtransfn_oid) &&
2820  peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
2821  ereport(ERROR,
2822  (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2823  errmsg("strictness of aggregate's forward and inverse transition functions must match")));
2824 
2825  /*
2826  * Moving aggregates use their own aggcontext.
2827  *
2828  * This is necessary because they might restart at different times, so we
2829  * might never be able to reset the shared context otherwise. We can't
2830  * make it the aggregates' responsibility to clean up after themselves,
2831  * because strict aggregates must be restarted whenever we remove their
2832  * last non-NULL input, which the aggregate won't be aware is happening.
2833  * Also, just pfree()ing the transValue upon restarting wouldn't help,
2834  * since we'd miss any indirectly referenced data. We could, in theory,
2835  * make the memory allocation rules for moving aggregates different than
2836  * they have historically been for plain aggregates, but that seems grotty
2837  * and likely to lead to memory leaks.
2838  */
2839  if (OidIsValid(invtransfn_oid))
2840  peraggstate->aggcontext =
2842  "WindowAgg Per Aggregate",
2844  else
2845  peraggstate->aggcontext = winstate->aggcontext;
2846 
2847  ReleaseSysCache(aggTuple);
2848 
2849  return peraggstate;
2850 }
2851 
2852 static Datum
2853 GetAggInitVal(Datum textInitVal, Oid transtype)
2854 {
2855  Oid typinput,
2856  typioparam;
2857  char *strInitVal;
2858  Datum initVal;
2859 
2860  getTypeInputInfo(transtype, &typinput, &typioparam);
2861  strInitVal = TextDatumGetCString(textInitVal);
2862  initVal = OidInputFunctionCall(typinput, strInitVal,
2863  typioparam, -1);
2864  pfree(strInitVal);
2865  return initVal;
2866 }
2867 
2868 /*
2869  * are_peers
2870  * compare two rows to see if they are equal according to the ORDER BY clause
2871  *
2872  * NB: this does not consider the window frame mode.
2873  */
2874 static bool
2876  TupleTableSlot *slot2)
2877 {
2878  WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2879  ExprContext *econtext = winstate->tmpcontext;
2880 
2881  /* If no ORDER BY, all rows are peers with each other */
2882  if (node->ordNumCols == 0)
2883  return true;
2884 
2885  econtext->ecxt_outertuple = slot1;
2886  econtext->ecxt_innertuple = slot2;
2887  return ExecQualAndReset(winstate->ordEqfunction, econtext);
2888 }
2889 
2890 /*
2891  * window_gettupleslot
2892  * Fetch the pos'th tuple of the current partition into the slot,
2893  * using the winobj's read pointer
2894  *
2895  * Returns true if successful, false if no such row
2896  */
2897 static bool
2899 {
2900  WindowAggState *winstate = winobj->winstate;
2901  MemoryContext oldcontext;
2902 
2903  /* often called repeatedly in a row */
2905 
2906  /* Don't allow passing -1 to spool_tuples here */
2907  if (pos < 0)
2908  return false;
2909 
2910  /* If necessary, fetch the tuple into the spool */
2911  spool_tuples(winstate, pos);
2912 
2913  if (pos >= winstate->spooled_rows)
2914  return false;
2915 
2916  if (pos < winobj->markpos)
2917  elog(ERROR, "cannot fetch row before WindowObject's mark position");
2918 
2920 
2921  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
2922 
2923  /*
2924  * Advance or rewind until we are within one tuple of the one we want.
2925  */
2926  if (winobj->seekpos < pos - 1)
2927  {
2928  if (!tuplestore_skiptuples(winstate->buffer,
2929  pos - 1 - winobj->seekpos,
2930  true))
2931  elog(ERROR, "unexpected end of tuplestore");
2932  winobj->seekpos = pos - 1;
2933  }
2934  else if (winobj->seekpos > pos + 1)
2935  {
2936  if (!tuplestore_skiptuples(winstate->buffer,
2937  winobj->seekpos - (pos + 1),
2938  false))
2939  elog(ERROR, "unexpected end of tuplestore");
2940  winobj->seekpos = pos + 1;
2941  }
2942  else if (winobj->seekpos == pos)
2943  {
2944  /*
2945  * There's no API to refetch the tuple at the current position. We
2946  * have to move one tuple forward, and then one backward. (We don't
2947  * do it the other way because we might try to fetch the row before
2948  * our mark, which isn't allowed.) XXX this case could stand to be
2949  * optimized.
2950  */
2951  tuplestore_advance(winstate->buffer, true);
2952  winobj->seekpos++;
2953  }
2954 
2955  /*
2956  * Now we should be on the tuple immediately before or after the one we
2957  * want, so just fetch forwards or backwards as appropriate.
2958  */
2959  if (winobj->seekpos > pos)
2960  {
2961  if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
2962  elog(ERROR, "unexpected end of tuplestore");
2963  winobj->seekpos--;
2964  }
2965  else
2966  {
2967  if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
2968  elog(ERROR, "unexpected end of tuplestore");
2969  winobj->seekpos++;
2970  }
2971 
2972  Assert(winobj->seekpos == pos);
2973 
2974  MemoryContextSwitchTo(oldcontext);
2975 
2976  return true;
2977 }
2978 
2979 
2980 /***********************************************************************
2981  * API exposed to window functions
2982  ***********************************************************************/
2983 
2984 
2985 /*
2986  * WinGetPartitionLocalMemory
2987  * Get working memory that lives till end of partition processing
2988  *
2989  * On first call within a given partition, this allocates and zeroes the
2990  * requested amount of space. Subsequent calls just return the same chunk.
2991  *
2992  * Memory obtained this way is normally used to hold state that should be
2993  * automatically reset for each new partition. If a window function wants
2994  * to hold state across the whole query, fcinfo->fn_extra can be used in the
2995  * usual way for that.
2996  */
2997 void *
2999 {
3000  Assert(WindowObjectIsValid(winobj));
3001  if (winobj->localmem == NULL)
3002  winobj->localmem =
3004  return winobj->localmem;
3005 }
3006 
3007 /*
3008  * WinGetCurrentPosition
3009  * Return the current row's position (counting from 0) within the current
3010  * partition.
3011  */
3012 int64
3014 {
3015  Assert(WindowObjectIsValid(winobj));
3016  return winobj->winstate->currentpos;
3017 }
3018 
3019 /*
3020  * WinGetPartitionRowCount
3021  * Return total number of rows contained in the current partition.
3022  *
3023  * Note: this is a relatively expensive operation because it forces the
3024  * whole partition to be "spooled" into the tuplestore at once. Once
3025  * executed, however, additional calls within the same partition are cheap.
3026  */
3027 int64
3029 {
3030  Assert(WindowObjectIsValid(winobj));
3031  spool_tuples(winobj->winstate, -1);
3032  return winobj->winstate->spooled_rows;
3033 }
3034 
3035 /*
3036  * WinSetMarkPosition
3037  * Set the "mark" position for the window object, which is the oldest row
3038  * number (counting from 0) it is allowed to fetch during all subsequent
3039  * operations within the current partition.
3040  *
3041  * Window functions do not have to call this, but are encouraged to move the
3042  * mark forward when possible to keep the tuplestore size down and prevent
3043  * having to spill rows to disk.
3044  */
3045 void
3047 {
3049 
3050  Assert(WindowObjectIsValid(winobj));
3051  winstate = winobj->winstate;
3052 
3053  if (markpos < winobj->markpos)
3054  elog(ERROR, "cannot move WindowObject's mark position backward");
3055  tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3056  if (markpos > winobj->markpos)
3057  {
3058  tuplestore_skiptuples(winstate->buffer,
3059  markpos - winobj->markpos,
3060  true);
3061  winobj->markpos = markpos;
3062  }
3063  tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3064  if (markpos > winobj->seekpos)
3065  {
3066  tuplestore_skiptuples(winstate->buffer,
3067  markpos - winobj->seekpos,
3068  true);
3069  winobj->seekpos = markpos;
3070  }
3071 }
3072 
3073 /*
3074  * WinRowsArePeers
3075  * Compare two rows (specified by absolute position in partition) to see
3076  * if they are equal according to the ORDER BY clause.
3077  *
3078  * NB: this does not consider the window frame mode.
3079  */
3080 bool
3081 WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
3082 {
3084  WindowAgg *node;
3085  TupleTableSlot *slot1;
3086  TupleTableSlot *slot2;
3087  bool res;
3088 
3089  Assert(WindowObjectIsValid(winobj));
3090  winstate = winobj->winstate;
3091  node = (WindowAgg *) winstate->ss.ps.plan;
3092 
3093  /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3094  if (node->ordNumCols == 0)
3095  return true;
3096 
3097  /*
3098  * Note: OK to use temp_slot_2 here because we aren't calling any
3099  * frame-related functions (those tend to clobber temp_slot_2).
3100  */
3101  slot1 = winstate->temp_slot_1;
3102  slot2 = winstate->temp_slot_2;
3103 
3104  if (!window_gettupleslot(winobj, pos1, slot1))
3105  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3106  pos1);
3107  if (!window_gettupleslot(winobj, pos2, slot2))
3108  elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3109  pos2);
3110 
3111  res = are_peers(winstate, slot1, slot2);
3112 
3113  ExecClearTuple(slot1);
3114  ExecClearTuple(slot2);
3115 
3116  return res;
3117 }
3118 
3119 /*
3120  * WinGetFuncArgInPartition
3121  * Evaluate a window function's argument expression on a specified
3122  * row of the partition. The row is identified in lseek(2) style,
3123  * i.e. relative to the current, first, or last row.
3124  *
3125  * argno: argument number to evaluate (counted from 0)
3126  * relpos: signed rowcount offset from the seek position
3127  * seektype: WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, or WINDOW_SEEK_TAIL
3128  * set_mark: If the row is found and set_mark is true, the mark is moved to
3129  * the row as a side-effect.
3130  * isnull: output argument, receives isnull status of result
3131  * isout: output argument, set to indicate whether target row position
3132  * is out of partition (can pass NULL if caller doesn't care about this)
3133  *
3134  * Specifying a nonexistent row is not an error, it just causes a null result
3135  * (plus setting *isout true, if isout isn't NULL).
3136  */
3137 Datum
3139  int relpos, int seektype, bool set_mark,
3140  bool *isnull, bool *isout)
3141 {
3143  ExprContext *econtext;
3144  TupleTableSlot *slot;
3145  bool gottuple;
3146  int64 abs_pos;
3147 
3148  Assert(WindowObjectIsValid(winobj));
3149  winstate = winobj->winstate;
3150  econtext = winstate->ss.ps.ps_ExprContext;
3151  slot = winstate->temp_slot_1;
3152 
3153  switch (seektype)
3154  {
3155  case WINDOW_SEEK_CURRENT:
3156  abs_pos = winstate->currentpos + relpos;
3157  break;
3158  case WINDOW_SEEK_HEAD:
3159  abs_pos = relpos;
3160  break;
3161  case WINDOW_SEEK_TAIL:
3162  spool_tuples(winstate, -1);
3163  abs_pos = winstate->spooled_rows - 1 + relpos;
3164  break;
3165  default:
3166  elog(ERROR, "unrecognized window seek type: %d", seektype);
3167  abs_pos = 0; /* keep compiler quiet */
3168  break;
3169  }
3170 
3171  gottuple = window_gettupleslot(winobj, abs_pos, slot);
3172 
3173  if (!gottuple)
3174  {
3175  if (isout)
3176  *isout = true;
3177  *isnull = true;
3178  return (Datum) 0;
3179  }
3180  else
3181  {
3182  if (isout)
3183  *isout = false;
3184  if (set_mark)
3185  WinSetMarkPosition(winobj, abs_pos);
3186  econtext->ecxt_outertuple = slot;
3187  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3188  econtext, isnull);
3189  }
3190 }
3191 
3192 /*
3193  * WinGetFuncArgInFrame
3194  * Evaluate a window function's argument expression on a specified
3195  * row of the window frame. The row is identified in lseek(2) style,
3196  * i.e. relative to the first or last row of the frame. (We do not
3197  * support WINDOW_SEEK_CURRENT here, because it's not very clear what
3198  * that should mean if the current row isn't part of the frame.)
3199  *
3200  * argno: argument number to evaluate (counted from 0)
3201  * relpos: signed rowcount offset from the seek position
3202  * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
3203  * set_mark: If the row is found/in frame and set_mark is true, the mark is
3204  * moved to the row as a side-effect.
3205  * isnull: output argument, receives isnull status of result
3206  * isout: output argument, set to indicate whether target row position
3207  * is out of frame (can pass NULL if caller doesn't care about this)
3208  *
3209  * Specifying a nonexistent or not-in-frame row is not an error, it just
3210  * causes a null result (plus setting *isout true, if isout isn't NULL).
3211  *
3212  * Note that some exclusion-clause options lead to situations where the
3213  * rows that are in-frame are not consecutive in the partition. But we
3214  * count only in-frame rows when measuring relpos.
3215  *
3216  * The set_mark flag is interpreted as meaning that the caller will specify
3217  * a constant (or, perhaps, monotonically increasing) relpos in successive
3218  * calls, so that *if there is no exclusion clause* there will be no need
3219  * to fetch a row before the previously fetched row. But we do not expect
3220  * the caller to know how to account for exclusion clauses. Therefore,
3221  * if there is an exclusion clause we take responsibility for adjusting the
3222  * mark request to something that will be safe given the above assumption
3223  * about relpos.
3224  */
3225 Datum
3227  int relpos, int seektype, bool set_mark,
3228  bool *isnull, bool *isout)
3229 {
3231  ExprContext *econtext;
3232  TupleTableSlot *slot;
3233  int64 abs_pos;
3234  int64 mark_pos;
3235 
3236  Assert(WindowObjectIsValid(winobj));
3237  winstate = winobj->winstate;
3238  econtext = winstate->ss.ps.ps_ExprContext;
3239  slot = winstate->temp_slot_1;
3240 
3241  switch (seektype)
3242  {
3243  case WINDOW_SEEK_CURRENT:
3244  elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3245  abs_pos = mark_pos = 0; /* keep compiler quiet */
3246  break;
3247  case WINDOW_SEEK_HEAD:
3248  /* rejecting relpos < 0 is easy and simplifies code below */
3249  if (relpos < 0)
3250  goto out_of_frame;
3251  update_frameheadpos(winstate);
3252  abs_pos = winstate->frameheadpos + relpos;
3253  mark_pos = abs_pos;
3254 
3255  /*
3256  * Account for exclusion option if one is active, but advance only
3257  * abs_pos not mark_pos. This prevents changes of the current
3258  * row's peer group from resulting in trying to fetch a row before
3259  * some previous mark position.
3260  *
3261  * Note that in some corner cases such as current row being
3262  * outside frame, these calculations are theoretically too simple,
3263  * but it doesn't matter because we'll end up deciding the row is
3264  * out of frame. We do not attempt to avoid fetching rows past
3265  * end of frame; that would happen in some cases anyway.
3266  */
3267  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3268  {
3269  case 0:
3270  /* no adjustment needed */
3271  break;
3273  if (abs_pos >= winstate->currentpos &&
3274  winstate->currentpos >= winstate->frameheadpos)
3275  abs_pos++;
3276  break;
3278  update_grouptailpos(winstate);
3279  if (abs_pos >= winstate->groupheadpos &&
3280  winstate->grouptailpos > winstate->frameheadpos)
3281  {
3282  int64 overlapstart = Max(winstate->groupheadpos,
3283  winstate->frameheadpos);
3284 
3285  abs_pos += winstate->grouptailpos - overlapstart;
3286  }
3287  break;
3289  update_grouptailpos(winstate);
3290  if (abs_pos >= winstate->groupheadpos &&
3291  winstate->grouptailpos > winstate->frameheadpos)
3292  {
3293  int64 overlapstart = Max(winstate->groupheadpos,
3294  winstate->frameheadpos);
3295 
3296  if (abs_pos == overlapstart)
3297  abs_pos = winstate->currentpos;
3298  else
3299  abs_pos += winstate->grouptailpos - overlapstart - 1;
3300  }
3301  break;
3302  default:
3303  elog(ERROR, "unrecognized frame option state: 0x%x",
3304  winstate->frameOptions);
3305  break;
3306  }
3307  break;
3308  case WINDOW_SEEK_TAIL:
3309  /* rejecting relpos > 0 is easy and simplifies code below */
3310  if (relpos > 0)
3311  goto out_of_frame;
3312  update_frametailpos(winstate);
3313  abs_pos = winstate->frametailpos - 1 + relpos;
3314 
3315  /*
3316  * Account for exclusion option if one is active. If there is no
3317  * exclusion, we can safely set the mark at the accessed row. But
3318  * if there is, we can only mark the frame start, because we can't
3319  * be sure how far back in the frame the exclusion might cause us
3320  * to fetch in future. Furthermore, we have to actually check
3321  * against frameheadpos here, since it's unsafe to try to fetch a
3322  * row before frame start if the mark might be there already.
3323  */
3324  switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3325  {
3326  case 0:
3327  /* no adjustment needed */
3328  mark_pos = abs_pos;
3329  break;
3331  if (abs_pos <= winstate->currentpos &&
3332  winstate->currentpos < winstate->frametailpos)
3333  abs_pos--;
3334  update_frameheadpos(winstate);
3335  if (abs_pos < winstate->frameheadpos)
3336  goto out_of_frame;
3337  mark_pos = winstate->frameheadpos;
3338  break;
3340  update_grouptailpos(winstate);
3341  if (abs_pos < winstate->grouptailpos &&
3342  winstate->groupheadpos < winstate->frametailpos)
3343  {
3344  int64 overlapend = Min(winstate->grouptailpos,
3345  winstate->frametailpos);
3346 
3347  abs_pos -= overlapend - winstate->groupheadpos;
3348  }
3349  update_frameheadpos(winstate);
3350  if (abs_pos < winstate->frameheadpos)
3351  goto out_of_frame;
3352  mark_pos = winstate->frameheadpos;
3353  break;
3355  update_grouptailpos(winstate);
3356  if (abs_pos < winstate->grouptailpos &&
3357  winstate->groupheadpos < winstate->frametailpos)
3358  {
3359  int64 overlapend = Min(winstate->grouptailpos,
3360  winstate->frametailpos);
3361 
3362  if (abs_pos == overlapend - 1)
3363  abs_pos = winstate->currentpos;
3364  else
3365  abs_pos -= overlapend - 1 - winstate->groupheadpos;
3366  }
3367  update_frameheadpos(winstate);
3368  if (abs_pos < winstate->frameheadpos)
3369  goto out_of_frame;
3370  mark_pos = winstate->frameheadpos;
3371  break;
3372  default:
3373  elog(ERROR, "unrecognized frame option state: 0x%x",
3374  winstate->frameOptions);
3375  mark_pos = 0; /* keep compiler quiet */
3376  break;
3377  }
3378  break;
3379  default:
3380  elog(ERROR, "unrecognized window seek type: %d", seektype);
3381  abs_pos = mark_pos = 0; /* keep compiler quiet */
3382  break;
3383  }
3384 
3385  if (!window_gettupleslot(winobj, abs_pos, slot))
3386  goto out_of_frame;
3387 
3388  /* The code above does not detect all out-of-frame cases, so check */
3389  if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3390  goto out_of_frame;
3391 
3392  if (isout)
3393  *isout = false;
3394  if (set_mark)
3395  WinSetMarkPosition(winobj, mark_pos);
3396  econtext->ecxt_outertuple = slot;
3397  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3398  econtext, isnull);
3399 
3400 out_of_frame:
3401  if (isout)
3402  *isout = true;
3403  *isnull = true;
3404  return (Datum) 0;
3405 }
3406 
3407 /*
3408  * WinGetFuncArgCurrent
3409  * Evaluate a window function's argument expression on the current row.
3410  *
3411  * argno: argument number to evaluate (counted from 0)
3412  * isnull: output argument, receives isnull status of result
3413  *
3414  * Note: this isn't quite equivalent to WinGetFuncArgInPartition or
3415  * WinGetFuncArgInFrame targeting the current row, because it will succeed
3416  * even if the WindowObject's mark has been set beyond the current row.
3417  * This should generally be used for "ordinary" arguments of a window
3418  * function, such as the offset argument of lead() or lag().
3419  */
3420 Datum
3421 WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
3422 {
3424  ExprContext *econtext;
3425 
3426  Assert(WindowObjectIsValid(winobj));
3427  winstate = winobj->winstate;
3428 
3429  econtext = winstate->ss.ps.ps_ExprContext;
3430 
3431  econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3432  return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3433  econtext, isnull);
3434 }
signed short int16
Definition: c.h:312
int ordNumCols
Definition: plannodes.h:820
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:708
#define NIL
Definition: pg_list.h:69
Datum WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
Definition: fmgr.h:56
List * qual
Definition: plannodes.h:147
bool WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
MemoryContext curaggcontext
Definition: execnodes.h:2000
ExprState * endOffset
Definition: execnodes.h:1980
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:528
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
void * WinGetPartitionLocalMemory(WindowObject winobj, Size sz)
Datum * ecxt_aggvalues
Definition: execnodes.h:237
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:296
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1110
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:1949
int64 WinGetPartitionRowCount(WindowObject winobj)
Datum startOffsetValue
Definition: execnodes.h:1981
static void finalize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
ExprState * aggfilter
Definition: execnodes.h:733
#define GETSTRUCT(TUP)
Definition: htup_details.h:668
static void update_grouptailpos(WindowAggState *winstate)
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:948
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
List * args
Definition: primnodes.h:360
Oid inRangeColl
Definition: plannodes.h:829
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:439
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2986
Oid GetUserId(void)
Definition: miscinit.c:379
#define castNode(_type_, nodeptr)
Definition: nodes.h:586
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1360
ScanState ss
Definition: execnodes.h:1953
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:538
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:359
int64 groupheadpos
Definition: execnodes.h:1995
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1207
ExprContext * ps_ExprContext
Definition: execnodes.h:947
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:226
AttrNumber * ordColIdx
Definition: plannodes.h:821
#define Min(x, y)
Definition: c.h:857
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: execTuples.c:475
static void update_frametailpos(WindowAggState *winstate)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int64 currentgroup
Definition: execnodes.h:1992
Definition: nodes.h:517
int64 aggregatedupto
Definition: execnodes.h:1976
int errcode(int sqlerrcode)
Definition: elog.c:575
#define MemSet(start, val, len)
Definition: c.h:908
bool frametail_valid
Definition: execnodes.h:2011
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1195
static void release_partition(WindowAggState *winstate)
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:1948
int64 frametailgroup
Definition: execnodes.h:1994
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:2038
int64 grouptailpos
Definition: execnodes.h:1996
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:79
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:958
EState * state
Definition: execnodes.h:914
static void update_frameheadpos(WindowAggState *winstate)
unsigned int Oid
Definition: postgres_ext.h:31
NodeTag
Definition: nodes.h:26
Index winref
Definition: primnodes.h:362
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:510
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2023
#define OidIsValid(objectId)
Definition: c.h:605
WindowStatePerFunc perfunc
Definition: execnodes.h:1960
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:566
Oid * ordOperators
Definition: plannodes.h:822
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:524
static void spool_tuples(WindowAggState *winstate, int64 pos)
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc)
Definition: execTuples.c:931
Oid endInRangeFunc
Definition: plannodes.h:828
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
TupleTableSlot * first_part_slot
Definition: execnodes.h:2016
static bool advance_windowaggregate_base(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
#define FUNC_MAX_ARGS
ExprContext * tmpcontext
Definition: execnodes.h:2001
PlanState ps
Definition: execnodes.h:1192
struct WindowObjectData * agg_winobj
Definition: execnodes.h:1974
bool inRangeNullsFirst
Definition: execnodes.h:1989
Node * startOffset
Definition: plannodes.h:824
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1455
int64 frameheadpos
Definition: execnodes.h:1971
FmgrInfo * flinfo
Definition: fmgr.h:79
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:521
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3349
void pfree(void *pointer)
Definition: mcxt.c:1031
ExprState * partEqfunction
Definition: execnodes.h:1962
#define ObjectIdGetDatum(X)
Definition: postgres.h:492
#define ERROR
Definition: elog.h:43
bool fn_strict
Definition: fmgr.h:61
Expr * expr
Definition: execnodes.h:90
FmgrInfo endInRangeFunc
Definition: execnodes.h:1986
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1397
struct WindowObjectData WindowObjectData
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate)
Definition: execUtils.c:598
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:124
#define DatumGetInt64(X)
Definition: postgres.h:592
struct WindowStatePerFuncData WindowStatePerFuncData
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
#define EXEC_FLAG_BACKWARD
Definition: executor.h:60
#define outerPlanState(node)
Definition: execnodes.h:966
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2024
Datum WinGetFuncArgInPartition(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
Datum endOffsetValue
Definition: execnodes.h:1982
void * list_nth(const List *list, int n)
Definition: list.c:410
WindowStatePerAgg peragg
Definition: execnodes.h:1961
ExprState * ordEqfunction
Definition: execnodes.h:1963
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:142
ExprState * startOffset
Definition: execnodes.h:1979
int64 frameheadgroup
Definition: execnodes.h:1993
static int row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:456
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:516
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:281
int64 aggregatedbase
Definition: execnodes.h:1975
Node * endOffset
Definition: plannodes.h:825
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:515
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:107
#define DatumGetBool(X)
Definition: postgres.h:378
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:220
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:513
#define TupIsNull(slot)
Definition: tuptable.h:146
int partNumCols
Definition: plannodes.h:817
Oid winfnoid
Definition: primnodes.h:356
bool argnull[FUNC_MAX_ARGS]
Definition: fmgr.h:89
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
Tuplestorestate * buffer
Definition: execnodes.h:1964
MemoryContext aggcontext
Definition: execnodes.h:1999
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:134
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:514
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2617
#define ereport(elevel, rest)
Definition: elog.h:122
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:128
Bitmapset * chgParam
Definition: execnodes.h:941
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:179
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define outerPlan(node)
Definition: plannodes.h:176
int64 WinGetCurrentPosition(WindowObject winobj)
#define AllocSetContextCreate(parent, name, allocparams)
Definition: memutils.h:170
bool inRangeAsc
Definition: plannodes.h:830
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
static void eval_windowaggregates(WindowAggState *winstate)
int64 spooled_rows
Definition: execnodes.h:1969
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1112
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:124
#define MemoryContextResetAndDeleteChildren(ctx)
Definition: memutils.h:67
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
bool * ecxt_aggnulls
Definition: execnodes.h:239
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:388
#define TextDatumGetCString(d)
Definition: builtins.h:96
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
WindowAggState * winstate
Definition: nodeWindowAgg.c:63
void * palloc0(Size size)
Definition: mcxt.c:955
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:918
AclResult
Definition: acl.h:178
uintptr_t Datum
Definition: postgres.h:367
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:520
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1160
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:233
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1368
int work_mem
Definition: globals.c:122
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2022
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
AttrNumber * partColIdx
Definition: plannodes.h:818
static struct @131 value
void ExecInitResultTupleSlotTL(EState *estate, PlanState *planstate)
Definition: execTuples.c:890
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:132
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:814
#define BoolGetDatum(X)
Definition: postgres.h:387
Plan * plan
Definition: execnodes.h:912
static void begin_partition(WindowAggState *winstate)
void DeleteExpandedObject(Datum d)
#define InvalidOid
Definition: postgres_ext.h:36
TupleTableSlot * frametail_slot
Definition: execnodes.h:2019
bool more_partitions
Definition: execnodes.h:2007
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:323
Datum arg[FUNC_MAX_ARGS]
Definition: fmgr.h:87
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41
#define Max(x, y)
Definition: c.h:851
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, AttrNumber *keyColIdx, Oid *eqOperators, PlanState *parent)
Definition: execGrouping.c:60
Oid * partOperators
Definition: plannodes.h:819
TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: execTuples.c:851
#define makeNode(_type_)
Definition: nodes.h:565
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
void ExecEndWindowAgg(WindowAggState *node)
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:222
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
int64 frametailpos
Definition: execnodes.h:1972
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:506
#define Assert(condition)
Definition: c.h:699
#define lfirst(lc)
Definition: pg_list.h:106
Index winref
Definition: plannodes.h:816
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:522
#define EXEC_FLAG_MARK
Definition: executor.h:61
FmgrInfo startInRangeFunc
Definition: execnodes.h:1985
bool grouptail_valid
Definition: execnodes.h:2013
TupleTableSlot * framehead_slot
Definition: execnodes.h:2018
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
bool inRangeNullsFirst
Definition: plannodes.h:831
bool MemoryContextContains(MemoryContext context, void *pointer)
Definition: mcxt.c:667
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:1901
size_t Size
Definition: c.h:433
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:428
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:125
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
static int list_length(const List *l)
Definition: pg_list.h:89
MemoryContext aggcontext
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:526
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2005
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:507
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1135
WindowAggState * ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:225
Datum WinGetFuncArgInFrame(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
#define INT64_FORMAT
Definition: c.h:367
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
int64 currentpos
Definition: execnodes.h:1970
ExprState * qual
Definition: execnodes.h:930
#define DatumGetPointer(X)
Definition: postgres.h:534
Oid startInRangeFunc
Definition: plannodes.h:827
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:383
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:517
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
bool partition_spooled
Definition: execnodes.h:2005
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool framehead_valid
Definition: execnodes.h:2009
#define ACL_EXECUTE
Definition: parsenodes.h:81
AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4651
int i
Plan plan
Definition: plannodes.h:815
Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: heaptuple.c:1518
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:473
struct WindowStatePerAggData WindowStatePerAggData
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:508
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
void ExecReScanWindowAgg(WindowAggState *node)
void * arg
MemoryContext partcontext
Definition: execnodes.h:1998
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:119
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define elog
Definition: elog.h:219
int frameOptions
Definition: plannodes.h:823
WindowFunc * wfunc
Definition: execnodes.h:731
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
Definition: pg_list.h:45
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1824
int16 AttrNumber
Definition: attnum.h:21
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:324
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
#define ResetExprContext(econtext)
Definition: executor.h:483
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:1846