PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-2026, 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"
39#include "catalog/pg_proc.h"
40#include "common/int.h"
41#include "executor/executor.h"
42#include "executor/instrument.h"
44#include "miscadmin.h"
45#include "nodes/nodeFuncs.h"
46#include "optimizer/clauses.h"
47#include "optimizer/optimizer.h"
48#include "parser/parse_agg.h"
49#include "parser/parse_coerce.h"
50#include "utils/acl.h"
51#include "utils/builtins.h"
52#include "utils/datum.h"
53#include "utils/expandeddatum.h"
54#include "utils/lsyscache.h"
55#include "utils/memutils.h"
56#include "utils/regproc.h"
57#include "utils/syscache.h"
58#include "utils/tuplestore.h"
59#include "windowapi.h"
60
61/*
62 * All the window function APIs are called with this object, which is passed
63 * to window functions as fcinfo->context.
64 */
65typedef struct WindowObjectData
66{
68 WindowAggState *winstate; /* parent WindowAggState */
69 List *argstates; /* ExprState trees for fn's arguments */
70 void *localmem; /* WinGetPartitionLocalMemory's chunk */
71 int markptr; /* tuplestore mark pointer for this fn */
72 int readptr; /* tuplestore read pointer for this fn */
73 int64 markpos; /* row that markptr is positioned on */
74 int64 seekpos; /* row that readptr is positioned on */
75 uint8 **notnull_info; /* not null info for each func args */
76 int64 *num_notnull_info; /* track size (number of tuples in
77 * partition) of the notnull_info array
78 * for each func args */
79 bool *notnull_info_cacheable; /* can we cache notnull_info? */
80
81 /*
82 * Null treatment options. One of: NO_NULLTREATMENT, PARSER_IGNORE_NULLS,
83 * PARSER_RESPECT_NULLS or IGNORE_NULLS.
84 */
87
88/*
89 * We have one WindowStatePerFunc struct for each window function and
90 * window aggregate handled by this node.
91 */
93{
94 /* Links to WindowFunc expr and state nodes this working state is for */
97
98 int numArguments; /* number of arguments */
99
100 FmgrInfo flinfo; /* fmgr lookup data for window function */
101
102 Oid winCollation; /* collation derived for window function */
103
104 /*
105 * We need the len and byval info for the result of each function in order
106 * to know how to copy/delete values.
107 */
110
111 bool plain_agg; /* is it just a plain aggregate function? */
112 int aggno; /* if so, index of its WindowStatePerAggData */
113 uint8 ignore_nulls; /* ignore nulls */
114
115 WindowObject winobj; /* object used in window function API */
117
118/*
119 * For plain aggregate window functions, we also have one of these.
120 */
122{
123 /* Oids of transition functions */
125 Oid invtransfn_oid; /* may be InvalidOid */
126 Oid finalfn_oid; /* may be InvalidOid */
127
128 /*
129 * fmgr lookup data for transition functions --- only valid when
130 * corresponding oid is not InvalidOid. Note in particular that fn_strict
131 * flags are kept here.
132 */
136
137 int numFinalArgs; /* number of arguments to pass to finalfn */
138
139 /*
140 * initial value from pg_aggregate entry
141 */
144
145 /*
146 * cached value for current frame boundaries
147 */
150
151 /*
152 * We need the len and byval info for the agg's input, result, and
153 * transition data types in order to know how to copy/delete values.
154 */
161
162 int wfuncno; /* index of associated WindowStatePerFuncData */
163
164 /* Context holding transition value and possibly other subsidiary data */
165 MemoryContext aggcontext; /* may be private, or winstate->aggcontext */
166
167 /* Current transition value */
168 Datum transValue; /* current transition value */
170
171 int64 transValueCount; /* number of currently-aggregated rows */
172
173 /* Data local to eval_windowaggregates() */
174 bool restart; /* need to restart this agg in this cycle? */
176
177static void initialize_windowaggregate(WindowAggState *winstate,
180static void advance_windowaggregate(WindowAggState *winstate,
183static bool advance_windowaggregate_base(WindowAggState *winstate,
186static void finalize_windowaggregate(WindowAggState *winstate,
189 Datum *result, bool *isnull);
190
191static void eval_windowaggregates(WindowAggState *winstate);
192static void eval_windowfunction(WindowAggState *winstate,
194 Datum *result, bool *isnull);
195
196static void begin_partition(WindowAggState *winstate);
197static void spool_tuples(WindowAggState *winstate, int64 pos);
198static void release_partition(WindowAggState *winstate);
199
200static int row_is_in_frame(WindowObject winobj, int64 pos,
201 TupleTableSlot *slot, bool fetch_tuple);
202static void update_frameheadpos(WindowAggState *winstate);
203static void update_frametailpos(WindowAggState *winstate);
204static void update_grouptailpos(WindowAggState *winstate);
205
207 WindowFunc *wfunc,
210
211static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
213static bool window_gettupleslot(WindowObject winobj, int64 pos,
214 TupleTableSlot *slot);
215
217 int relpos, int seektype,
218 bool set_mark, bool *isnull,
219 bool *isout);
221 int64 abs_pos, bool *isnull,
222 bool *isout);
223static void init_notnull_info(WindowObject winobj,
225static void grow_notnull_info(WindowObject winobj,
226 int64 pos, int argno);
228 int64 pos, int argno);
229static void put_notnull_info(WindowObject winobj,
230 int64 pos, int argno, bool isnull);
231
232/*
233 * Not null info bit array consists of 2-bit items
234 */
235#define NN_UNKNOWN 0x00 /* value not calculated yet */
236#define NN_NULL 0x01 /* NULL */
237#define NN_NOTNULL 0x02 /* NOT NULL */
238#define NN_MASK 0x03 /* mask for NOT NULL MAP */
239#define NN_BITS_PER_MEMBER 2 /* number of bits in not null map */
240/* number of items per variable */
241#define NN_ITEM_PER_VAR (BITS_PER_BYTE / NN_BITS_PER_MEMBER)
242/* convert map position to byte offset */
243#define NN_POS_TO_BYTES(pos) ((pos) / NN_ITEM_PER_VAR)
244/* bytes offset to map position */
245#define NN_BYTES_TO_POS(bytes) ((bytes) * NN_ITEM_PER_VAR)
246/* calculate shift bits */
247#define NN_SHIFT(pos) ((pos) % NN_ITEM_PER_VAR) * NN_BITS_PER_MEMBER
248
249/*
250 * initialize_windowaggregate
251 * parallel to initialize_aggregates in nodeAgg.c
252 */
253static void
257{
259
260 /*
261 * If we're using a private aggcontext, we may reset it here. But if the
262 * context is shared, we don't know which other aggregates may still need
263 * it, so we must leave it to the caller to reset at an appropriate time.
264 */
265 if (peraggstate->aggcontext != winstate->aggcontext)
266 MemoryContextReset(peraggstate->aggcontext);
267
268 if (peraggstate->initValueIsNull)
269 peraggstate->transValue = peraggstate->initValue;
270 else
271 {
273 peraggstate->transValue = datumCopy(peraggstate->initValue,
274 peraggstate->transtypeByVal,
275 peraggstate->transtypeLen);
277 }
278 peraggstate->transValueIsNull = peraggstate->initValueIsNull;
279 peraggstate->transValueCount = 0;
280 peraggstate->resultValue = (Datum) 0;
281 peraggstate->resultValueIsNull = true;
282}
283
284/*
285 * advance_windowaggregate
286 * parallel to advance_aggregates in nodeAgg.c
287 */
288static void
292{
294 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
295 int numArguments = perfuncstate->numArguments;
296 Datum newVal;
297 ListCell *arg;
298 int i;
300 ExprContext *econtext = winstate->tmpcontext;
301 ExprState *filter = wfuncstate->aggfilter;
302
304
305 /* Skip anything FILTERed out */
306 if (filter)
307 {
308 bool isnull;
309 Datum res = ExecEvalExpr(filter, econtext, &isnull);
310
311 if (isnull || !DatumGetBool(res))
312 {
314 return;
315 }
316 }
317
318 /* We start from 1, since the 0th arg will be the transition value */
319 i = 1;
320 foreach(arg, wfuncstate->args)
321 {
323
324 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
325 &fcinfo->args[i].isnull);
326 i++;
327 }
328
329 if (peraggstate->transfn.fn_strict)
330 {
331 /*
332 * For a strict transfn, nothing happens when there's a NULL input; we
333 * just keep the prior transValue. Note transValueCount doesn't
334 * change either.
335 */
336 for (i = 1; i <= numArguments; i++)
337 {
338 if (fcinfo->args[i].isnull)
339 {
341 return;
342 }
343 }
344
345 /*
346 * For strict transition functions with initial value NULL we use the
347 * first non-NULL input as the initial state. (We already checked
348 * that the agg's input type is binary-compatible with its transtype,
349 * so straight copy here is OK.)
350 *
351 * We must copy the datum into aggcontext if it is pass-by-ref. We do
352 * not need to pfree the old transValue, since it's NULL.
353 */
354 if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
355 {
357 peraggstate->transValue = datumCopy(fcinfo->args[1].value,
358 peraggstate->transtypeByVal,
359 peraggstate->transtypeLen);
360 peraggstate->transValueIsNull = false;
361 peraggstate->transValueCount = 1;
363 return;
364 }
365
366 if (peraggstate->transValueIsNull)
367 {
368 /*
369 * Don't call a strict function with NULL inputs. Note it is
370 * possible to get here despite the above tests, if the transfn is
371 * strict *and* returned a NULL on a prior cycle. If that happens
372 * we will propagate the NULL all the way to the end. That can
373 * only happen if there's no inverse transition function, though,
374 * since we disallow transitions back to NULL when there is one.
375 */
377 Assert(!OidIsValid(peraggstate->invtransfn_oid));
378 return;
379 }
380 }
381
382 /*
383 * OK to call the transition function. Set winstate->curaggcontext while
384 * calling it, for possible use by AggCheckCallContext.
385 */
386 InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
387 numArguments + 1,
388 perfuncstate->winCollation,
389 (Node *) winstate, NULL);
390 fcinfo->args[0].value = peraggstate->transValue;
391 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
392 winstate->curaggcontext = peraggstate->aggcontext;
393 newVal = FunctionCallInvoke(fcinfo);
394 winstate->curaggcontext = NULL;
395
396 /*
397 * Moving-aggregate transition functions must not return null, see
398 * advance_windowaggregate_base().
399 */
400 if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
403 errmsg("moving-aggregate transition function must not return null")));
404
405 /*
406 * We must track the number of rows included in transValue, since to
407 * remove the last input, advance_windowaggregate_base() mustn't call the
408 * inverse transition function, but simply reset transValue back to its
409 * initial value.
410 */
411 peraggstate->transValueCount++;
412
413 /*
414 * If pass-by-ref datatype, must copy the new value into aggcontext and
415 * free the prior transValue. But if transfn returned a pointer to its
416 * first input, we don't need to do anything. Also, if transfn returned a
417 * pointer to a R/W expanded object that is already a child of the
418 * aggcontext, assume we can adopt that value without copying it. (See
419 * comments for ExecAggCopyTransValue, which this code duplicates.)
420 */
421 if (!peraggstate->transtypeByVal &&
422 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
423 {
424 if (!fcinfo->isnull)
425 {
428 false,
429 peraggstate->transtypeLen) &&
431 /* do nothing */ ;
432 else
433 newVal = datumCopy(newVal,
434 peraggstate->transtypeByVal,
435 peraggstate->transtypeLen);
436 }
437 if (!peraggstate->transValueIsNull)
438 {
440 false,
441 peraggstate->transtypeLen))
443 else
444 pfree(DatumGetPointer(peraggstate->transValue));
445 }
446 }
447
449 peraggstate->transValue = newVal;
450 peraggstate->transValueIsNull = fcinfo->isnull;
451}
452
453/*
454 * advance_windowaggregate_base
455 * Remove the oldest tuple from an aggregation.
456 *
457 * This is very much like advance_windowaggregate, except that we will call
458 * the inverse transition function (which caller must have checked is
459 * available).
460 *
461 * Returns true if we successfully removed the current row from this
462 * aggregate, false if not (in the latter case, caller is responsible
463 * for cleaning up by restarting the aggregation).
464 */
465static bool
469{
471 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
472 int numArguments = perfuncstate->numArguments;
473 Datum newVal;
474 ListCell *arg;
475 int i;
477 ExprContext *econtext = winstate->tmpcontext;
478 ExprState *filter = wfuncstate->aggfilter;
479
481
482 /* Skip anything FILTERed out */
483 if (filter)
484 {
485 bool isnull;
486 Datum res = ExecEvalExpr(filter, econtext, &isnull);
487
488 if (isnull || !DatumGetBool(res))
489 {
491 return true;
492 }
493 }
494
495 /* We start from 1, since the 0th arg will be the transition value */
496 i = 1;
497 foreach(arg, wfuncstate->args)
498 {
500
501 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
502 &fcinfo->args[i].isnull);
503 i++;
504 }
505
506 if (peraggstate->invtransfn.fn_strict)
507 {
508 /*
509 * For a strict (inv)transfn, nothing happens when there's a NULL
510 * input; we just keep the prior transValue. Note transValueCount
511 * doesn't change either.
512 */
513 for (i = 1; i <= numArguments; i++)
514 {
515 if (fcinfo->args[i].isnull)
516 {
518 return true;
519 }
520 }
521 }
522
523 /* There should still be an added but not yet removed value */
524 Assert(peraggstate->transValueCount > 0);
525
526 /*
527 * In moving-aggregate mode, the state must never be NULL, except possibly
528 * before any rows have been aggregated (which is surely not the case at
529 * this point). This restriction allows us to interpret a NULL result
530 * from the inverse function as meaning "sorry, can't do an inverse
531 * transition in this case". We already checked this in
532 * advance_windowaggregate, but just for safety, check again.
533 */
534 if (peraggstate->transValueIsNull)
535 elog(ERROR, "aggregate transition value is NULL before inverse transition");
536
537 /*
538 * We mustn't use the inverse transition function to remove the last
539 * input. Doing so would yield a non-NULL state, whereas we should be in
540 * the initial state afterwards which may very well be NULL. So instead,
541 * we simply re-initialize the aggregate in this case.
542 */
543 if (peraggstate->transValueCount == 1)
544 {
547 &winstate->perfunc[peraggstate->wfuncno],
549 return true;
550 }
551
552 /*
553 * OK to call the inverse transition function. Set
554 * winstate->curaggcontext while calling it, for possible use by
555 * AggCheckCallContext.
556 */
557 InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
558 numArguments + 1,
559 perfuncstate->winCollation,
560 (Node *) winstate, NULL);
561 fcinfo->args[0].value = peraggstate->transValue;
562 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
563 winstate->curaggcontext = peraggstate->aggcontext;
564 newVal = FunctionCallInvoke(fcinfo);
565 winstate->curaggcontext = NULL;
566
567 /*
568 * If the function returns NULL, report failure, forcing a restart.
569 */
570 if (fcinfo->isnull)
571 {
573 return false;
574 }
575
576 /* Update number of rows included in transValue */
577 peraggstate->transValueCount--;
578
579 /*
580 * If pass-by-ref datatype, must copy the new value into aggcontext and
581 * free the prior transValue. But if invtransfn returned a pointer to its
582 * first input, we don't need to do anything. Also, if invtransfn
583 * returned a pointer to a R/W expanded object that is already a child of
584 * the aggcontext, assume we can adopt that value without copying it. (See
585 * comments for ExecAggCopyTransValue, which this code duplicates.)
586 *
587 * Note: the checks for null values here will never fire, but it seems
588 * best to have this stanza look just like advance_windowaggregate.
589 */
590 if (!peraggstate->transtypeByVal &&
591 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
592 {
593 if (!fcinfo->isnull)
594 {
597 false,
598 peraggstate->transtypeLen) &&
600 /* do nothing */ ;
601 else
602 newVal = datumCopy(newVal,
603 peraggstate->transtypeByVal,
604 peraggstate->transtypeLen);
605 }
606 if (!peraggstate->transValueIsNull)
607 {
609 false,
610 peraggstate->transtypeLen))
612 else
613 pfree(DatumGetPointer(peraggstate->transValue));
614 }
615 }
616
618 peraggstate->transValue = newVal;
619 peraggstate->transValueIsNull = fcinfo->isnull;
620
621 return true;
622}
623
624/*
625 * finalize_windowaggregate
626 * parallel to finalize_aggregate in nodeAgg.c
627 */
628static void
632 Datum *result, bool *isnull)
633{
635
637
638 /*
639 * Apply the agg's finalfn if one is provided, else return transValue.
640 */
641 if (OidIsValid(peraggstate->finalfn_oid))
642 {
644 int numFinalArgs = peraggstate->numFinalArgs;
645 bool anynull;
646 int i;
647
649 numFinalArgs,
650 perfuncstate->winCollation,
651 (Node *) winstate, NULL);
652 fcinfo->args[0].value =
654 peraggstate->transValueIsNull,
655 peraggstate->transtypeLen);
656 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
657 anynull = peraggstate->transValueIsNull;
658
659 /* Fill any remaining argument positions with nulls */
660 for (i = 1; i < numFinalArgs; i++)
661 {
662 fcinfo->args[i].value = (Datum) 0;
663 fcinfo->args[i].isnull = true;
664 anynull = true;
665 }
666
667 if (fcinfo->flinfo->fn_strict && anynull)
668 {
669 /* don't call a strict function with NULL inputs */
670 *result = (Datum) 0;
671 *isnull = true;
672 }
673 else
674 {
675 Datum res;
676
677 winstate->curaggcontext = peraggstate->aggcontext;
678 res = FunctionCallInvoke(fcinfo);
679 winstate->curaggcontext = NULL;
680 *isnull = fcinfo->isnull;
682 fcinfo->isnull,
683 peraggstate->resulttypeLen);
684 }
685 }
686 else
687 {
688 *result =
690 peraggstate->transValueIsNull,
691 peraggstate->transtypeLen);
692 *isnull = peraggstate->transValueIsNull;
693 }
694
696}
697
698/*
699 * eval_windowaggregates
700 * evaluate plain aggregates being used as window functions
701 *
702 * This differs from nodeAgg.c in two ways. First, if the window's frame
703 * start position moves, we use the inverse transition function (if it exists)
704 * to remove rows from the transition value. And second, we expect to be
705 * able to call aggregate final functions repeatedly after aggregating more
706 * data onto the same transition value. This is not a behavior required by
707 * nodeAgg.c.
708 */
709static void
711{
713 int wfuncno,
714 numaggs,
716 i;
719 ExprContext *econtext;
720 WindowObject agg_winobj;
721 TupleTableSlot *agg_row_slot;
723
724 numaggs = winstate->numaggs;
725 if (numaggs == 0)
726 return; /* nothing to do */
727
728 /* final output execution is in ps_ExprContext */
729 econtext = winstate->ss.ps.ps_ExprContext;
730 agg_winobj = winstate->agg_winobj;
731 agg_row_slot = winstate->agg_row_slot;
732 temp_slot = winstate->temp_slot_1;
733
734 /*
735 * If the window's frame start clause is UNBOUNDED_PRECEDING and no
736 * exclusion clause is specified, then the window frame consists of a
737 * contiguous group of rows extending forward from the start of the
738 * partition, and rows only enter the frame, never exit it, as the current
739 * row advances forward. This makes it possible to use an incremental
740 * strategy for evaluating aggregates: we run the transition function for
741 * each row added to the frame, and run the final function whenever we
742 * need the current aggregate value. This is considerably more efficient
743 * than the naive approach of re-running the entire aggregate calculation
744 * for each current row. It does assume that the final function doesn't
745 * damage the running transition value, but we have the same assumption in
746 * nodeAgg.c too (when it rescans an existing hash table).
747 *
748 * If the frame start does sometimes move, we can still optimize as above
749 * whenever successive rows share the same frame head, but if the frame
750 * head moves beyond the previous head we try to remove those rows using
751 * the aggregate's inverse transition function. This function restores
752 * the aggregate's current state to what it would be if the removed row
753 * had never been aggregated in the first place. Inverse transition
754 * functions may optionally return NULL, indicating that the function was
755 * unable to remove the tuple from aggregation. If this happens, or if
756 * the aggregate doesn't have an inverse transition function at all, we
757 * must perform the aggregation all over again for all tuples within the
758 * new frame boundaries.
759 *
760 * If there's any exclusion clause, then we may have to aggregate over a
761 * non-contiguous set of rows, so we punt and recalculate for every row.
762 * (For some frame end choices, it might be that the frame is always
763 * contiguous anyway, but that's an optimization to investigate later.)
764 *
765 * In many common cases, multiple rows share the same frame and hence the
766 * same aggregate value. (In particular, if there's no ORDER BY in a RANGE
767 * window, then all rows are peers and so they all have window frame equal
768 * to the whole partition.) We optimize such cases by calculating the
769 * aggregate value once when we reach the first row of a peer group, and
770 * then returning the saved value for all subsequent rows.
771 *
772 * 'aggregatedupto' keeps track of the first row that has not yet been
773 * accumulated into the aggregate transition values. Whenever we start a
774 * new peer group, we accumulate forward to the end of the peer group.
775 */
776
777 /*
778 * First, update the frame head position.
779 *
780 * The frame head should never move backwards, and the code below wouldn't
781 * cope if it did, so for safety we complain if it does.
782 */
783 update_frameheadpos(winstate);
784 if (winstate->frameheadpos < winstate->aggregatedbase)
785 elog(ERROR, "window frame head moved backward");
786
787 /*
788 * If the frame didn't change compared to the previous row, we can re-use
789 * the result values that were previously saved at the bottom of this
790 * function. Since we don't know the current frame's end yet, this is not
791 * possible to check for fully. But if the frame end mode is UNBOUNDED
792 * FOLLOWING or CURRENT ROW, no exclusion clause is specified, and the
793 * current row lies within the previous row's frame, then the two frames'
794 * ends must coincide. Note that on the first row aggregatedbase ==
795 * aggregatedupto, meaning this test must fail, so we don't need to check
796 * the "there was no previous row" case explicitly here.
797 */
798 if (winstate->aggregatedbase == winstate->frameheadpos &&
801 !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
802 winstate->aggregatedbase <= winstate->currentpos &&
803 winstate->aggregatedupto > winstate->currentpos)
804 {
805 for (i = 0; i < numaggs; i++)
806 {
807 peraggstate = &winstate->peragg[i];
808 wfuncno = peraggstate->wfuncno;
809 econtext->ecxt_aggvalues[wfuncno] = peraggstate->resultValue;
810 econtext->ecxt_aggnulls[wfuncno] = peraggstate->resultValueIsNull;
811 }
812 return;
813 }
814
815 /*----------
816 * Initialize restart flags.
817 *
818 * We restart the aggregation:
819 * - if we're processing the first row in the partition, or
820 * - if the frame's head moved and we cannot use an inverse
821 * transition function, or
822 * - we have an EXCLUSION clause, or
823 * - if the new frame doesn't overlap the old one
824 *
825 * Note that we don't strictly need to restart in the last case, but if
826 * we're going to remove all rows from the aggregation anyway, a restart
827 * surely is faster.
828 *----------
829 */
830 numaggs_restart = 0;
831 for (i = 0; i < numaggs; i++)
832 {
833 peraggstate = &winstate->peragg[i];
834 if (winstate->currentpos == 0 ||
835 (winstate->aggregatedbase != winstate->frameheadpos &&
836 !OidIsValid(peraggstate->invtransfn_oid)) ||
837 (winstate->frameOptions & FRAMEOPTION_EXCLUSION) ||
838 winstate->aggregatedupto <= winstate->frameheadpos)
839 {
840 peraggstate->restart = true;
842 }
843 else
844 peraggstate->restart = false;
845 }
846
847 /*
848 * If we have any possibly-moving aggregates, attempt to advance
849 * aggregatedbase to match the frame's head by removing input rows that
850 * fell off the top of the frame from the aggregations. This can fail,
851 * i.e. advance_windowaggregate_base() can return false, in which case
852 * we'll restart that aggregate below.
853 */
854 while (numaggs_restart < numaggs &&
855 winstate->aggregatedbase < winstate->frameheadpos)
856 {
857 /*
858 * Fetch the next tuple of those being removed. This should never fail
859 * as we should have been here before.
860 */
861 if (!window_gettupleslot(agg_winobj, winstate->aggregatedbase,
862 temp_slot))
863 elog(ERROR, "could not re-fetch previously fetched frame row");
864
865 /* Set tuple context for evaluation of aggregate arguments */
867
868 /*
869 * Perform the inverse transition for each aggregate function in the
870 * window, unless it has already been marked as needing a restart.
871 */
872 for (i = 0; i < numaggs; i++)
873 {
874 bool ok;
875
876 peraggstate = &winstate->peragg[i];
877 if (peraggstate->restart)
878 continue;
879
880 wfuncno = peraggstate->wfuncno;
882 &winstate->perfunc[wfuncno],
884 if (!ok)
885 {
886 /* Inverse transition function has failed, must restart */
887 peraggstate->restart = true;
889 }
890 }
891
892 /* Reset per-input-tuple context after each tuple */
893 ResetExprContext(winstate->tmpcontext);
894
895 /* And advance the aggregated-row state */
896 winstate->aggregatedbase++;
898 }
899
900 /*
901 * If we successfully advanced the base rows of all the aggregates,
902 * aggregatedbase now equals frameheadpos; but if we failed for any, we
903 * must forcibly update aggregatedbase.
904 */
905 winstate->aggregatedbase = winstate->frameheadpos;
906
907 /*
908 * If we created a mark pointer for aggregates, keep it pushed up to frame
909 * head, so that tuplestore can discard unnecessary rows.
910 */
911 if (agg_winobj->markptr >= 0)
912 WinSetMarkPosition(agg_winobj, winstate->frameheadpos);
913
914 /*
915 * Now restart the aggregates that require it.
916 *
917 * We assume that aggregates using the shared context always restart if
918 * *any* aggregate restarts, and we may thus clean up the shared
919 * aggcontext if that is the case. Private aggcontexts are reset by
920 * initialize_windowaggregate() if their owning aggregate restarts. If we
921 * aren't restarting an aggregate, we need to free any previously saved
922 * result for it, else we'll leak memory.
923 */
924 if (numaggs_restart > 0)
926 for (i = 0; i < numaggs; i++)
927 {
928 peraggstate = &winstate->peragg[i];
929
930 /* Aggregates using the shared ctx must restart if *any* agg does */
931 Assert(peraggstate->aggcontext != winstate->aggcontext ||
932 numaggs_restart == 0 ||
933 peraggstate->restart);
934
935 if (peraggstate->restart)
936 {
937 wfuncno = peraggstate->wfuncno;
939 &winstate->perfunc[wfuncno],
941 }
942 else if (!peraggstate->resultValueIsNull)
943 {
944 if (!peraggstate->resulttypeByVal)
945 pfree(DatumGetPointer(peraggstate->resultValue));
946 peraggstate->resultValue = (Datum) 0;
947 peraggstate->resultValueIsNull = true;
948 }
949 }
950
951 /*
952 * Non-restarted aggregates now contain the rows between aggregatedbase
953 * (i.e., frameheadpos) and aggregatedupto, while restarted aggregates
954 * contain no rows. If there are any restarted aggregates, we must thus
955 * begin aggregating anew at frameheadpos, otherwise we may simply
956 * continue at aggregatedupto. We must remember the old value of
957 * aggregatedupto to know how long to skip advancing non-restarted
958 * aggregates. If we modify aggregatedupto, we must also clear
959 * agg_row_slot, per the loop invariant below.
960 */
962 if (numaggs_restart > 0 &&
963 winstate->aggregatedupto != winstate->frameheadpos)
964 {
965 winstate->aggregatedupto = winstate->frameheadpos;
966 ExecClearTuple(agg_row_slot);
967 }
968
969 /*
970 * Advance until we reach a row not in frame (or end of partition).
971 *
972 * Note the loop invariant: agg_row_slot is either empty or holds the row
973 * at position aggregatedupto. We advance aggregatedupto after processing
974 * a row.
975 */
976 for (;;)
977 {
978 int ret;
979
980 /* Fetch next row if we didn't already */
981 if (TupIsNull(agg_row_slot))
982 {
983 if (!window_gettupleslot(agg_winobj, winstate->aggregatedupto,
984 agg_row_slot))
985 break; /* must be end of partition */
986 }
987
988 /*
989 * Exit loop if no more rows can be in frame. Skip aggregation if
990 * current row is not in frame but there might be more in the frame.
991 */
992 ret = row_is_in_frame(agg_winobj, winstate->aggregatedupto,
993 agg_row_slot, false);
994 if (ret < 0)
995 break;
996 if (ret == 0)
997 goto next_tuple;
998
999 /* Set tuple context for evaluation of aggregate arguments */
1000 winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
1001
1002 /* Accumulate row into the aggregates */
1003 for (i = 0; i < numaggs; i++)
1004 {
1005 peraggstate = &winstate->peragg[i];
1006
1007 /* Non-restarted aggs skip until aggregatedupto_nonrestarted */
1008 if (!peraggstate->restart &&
1010 continue;
1011
1012 wfuncno = peraggstate->wfuncno;
1013 advance_windowaggregate(winstate,
1014 &winstate->perfunc[wfuncno],
1015 peraggstate);
1016 }
1017
1018next_tuple:
1019 /* Reset per-input-tuple context after each tuple */
1020 ResetExprContext(winstate->tmpcontext);
1021
1022 /* And advance the aggregated-row state */
1023 winstate->aggregatedupto++;
1024 ExecClearTuple(agg_row_slot);
1025 }
1026
1027 /* The frame's end is not supposed to move backwards, ever */
1029
1030 /*
1031 * finalize aggregates and fill result/isnull fields.
1032 */
1033 for (i = 0; i < numaggs; i++)
1034 {
1035 Datum *result;
1036 bool *isnull;
1037
1038 peraggstate = &winstate->peragg[i];
1039 wfuncno = peraggstate->wfuncno;
1040 result = &econtext->ecxt_aggvalues[wfuncno];
1041 isnull = &econtext->ecxt_aggnulls[wfuncno];
1042 finalize_windowaggregate(winstate,
1043 &winstate->perfunc[wfuncno],
1045 result, isnull);
1046
1047 /*
1048 * save the result in case next row shares the same frame.
1049 *
1050 * XXX in some framing modes, eg ROWS/END_CURRENT_ROW, we can know in
1051 * advance that the next row can't possibly share the same frame. Is
1052 * it worth detecting that and skipping this code?
1053 */
1054 if (!peraggstate->resulttypeByVal && !*isnull)
1055 {
1057 peraggstate->resultValue =
1059 peraggstate->resulttypeByVal,
1060 peraggstate->resulttypeLen);
1062 }
1063 else
1064 {
1065 peraggstate->resultValue = *result;
1066 }
1067 peraggstate->resultValueIsNull = *isnull;
1068 }
1069}
1070
1071/*
1072 * eval_windowfunction
1073 *
1074 * Arguments of window functions are not evaluated here, because a window
1075 * function can need random access to arbitrary rows in the partition.
1076 * The window function uses the special WinGetFuncArgInPartition and
1077 * WinGetFuncArgInFrame functions to evaluate the arguments for the rows
1078 * it wants.
1079 */
1080static void
1082 Datum *result, bool *isnull)
1083{
1084 LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1086
1088
1089 /*
1090 * We don't pass any normal arguments to a window function, but we do pass
1091 * it the number of arguments, in order to permit window function
1092 * implementations to support varying numbers of arguments. The real info
1093 * goes through the WindowObject, which is passed via fcinfo->context.
1094 */
1095 InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1096 perfuncstate->numArguments,
1097 perfuncstate->winCollation,
1098 (Node *) perfuncstate->winobj, NULL);
1099 /* Just in case, make all the regular argument slots be null */
1100 for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1101 fcinfo->args[argno].isnull = true;
1102 /* Window functions don't have a current aggregate context, either */
1103 winstate->curaggcontext = NULL;
1104
1105 *result = FunctionCallInvoke(fcinfo);
1106 *isnull = fcinfo->isnull;
1107
1108 /*
1109 * The window function might have returned a pass-by-ref result that's
1110 * just a pointer into one of the WindowObject's temporary slots. That's
1111 * not a problem if it's the only window function using the WindowObject;
1112 * but if there's more than one function, we'd better copy the result to
1113 * ensure it's not clobbered by later window functions.
1114 */
1115 if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1116 winstate->numfuncs > 1)
1118 perfuncstate->resulttypeByVal,
1119 perfuncstate->resulttypeLen);
1120
1122}
1123
1124/*
1125 * prepare_tuplestore
1126 * Prepare the tuplestore and all of the required read pointers for the
1127 * WindowAggState's frameOptions.
1128 *
1129 * Note: We use pg_noinline to avoid bloating the calling function with code
1130 * which is only called once.
1131 */
1132static pg_noinline void
1134{
1135 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1136 int frameOptions = winstate->frameOptions;
1137 int numfuncs = winstate->numfuncs;
1138
1139 /* we shouldn't be called if this was done already */
1140 Assert(winstate->buffer == NULL);
1141
1142 /* Create new tuplestore */
1143 winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1144
1145 /*
1146 * Set up read pointers for the tuplestore. The current pointer doesn't
1147 * need BACKWARD capability, but the per-window-function read pointers do,
1148 * and the aggregate pointer does if we might need to restart aggregation.
1149 */
1150 winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1151
1152 /* reset default REWIND capability bit for current ptr */
1153 tuplestore_set_eflags(winstate->buffer, 0);
1154
1155 /* create read pointers for aggregates, if needed */
1156 if (winstate->numaggs > 0)
1157 {
1158 WindowObject agg_winobj = winstate->agg_winobj;
1159 int readptr_flags = 0;
1160
1161 /*
1162 * If the frame head is potentially movable, or we have an EXCLUSION
1163 * clause, we might need to restart aggregation ...
1164 */
1165 if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1166 (frameOptions & FRAMEOPTION_EXCLUSION))
1167 {
1168 /* ... so create a mark pointer to track the frame head */
1169 agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1170 /* and the read pointer will need BACKWARD capability */
1172 }
1173
1174 agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1176 }
1177
1178 /* create mark and read pointers for each real window function */
1179 for (int i = 0; i < numfuncs; i++)
1180 {
1181 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1182
1183 if (!perfuncstate->plain_agg)
1184 {
1185 WindowObject winobj = perfuncstate->winobj;
1186
1187 winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1188 0);
1189 winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1191 }
1192 }
1193
1194 /*
1195 * If we are in RANGE or GROUPS mode, then determining frame boundaries
1196 * requires physical access to the frame endpoint rows, except in certain
1197 * degenerate cases. We create read pointers to point to those rows, to
1198 * simplify access and ensure that the tuplestore doesn't discard the
1199 * endpoint rows prematurely. (Must create pointers in exactly the same
1200 * cases that update_frameheadpos and update_frametailpos need them.)
1201 */
1202 winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1203
1204 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1205 {
1206 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1207 node->ordNumCols != 0) ||
1208 (frameOptions & FRAMEOPTION_START_OFFSET))
1209 winstate->framehead_ptr =
1211 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1212 node->ordNumCols != 0) ||
1213 (frameOptions & FRAMEOPTION_END_OFFSET))
1214 winstate->frametail_ptr =
1216 }
1217
1218 /*
1219 * If we have an exclusion clause that requires knowing the boundaries of
1220 * the current row's peer group, we create a read pointer to track the
1221 * tail position of the peer group (i.e., first row of the next peer
1222 * group). The head position does not require its own pointer because we
1223 * maintain that as a side effect of advancing the current row.
1224 */
1225 winstate->grouptail_ptr = -1;
1226
1227 if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1229 node->ordNumCols != 0)
1230 {
1231 winstate->grouptail_ptr =
1233 }
1234}
1235
1236/*
1237 * begin_partition
1238 * Start buffering rows of the next partition.
1239 */
1240static void
1242{
1243 PlanState *outerPlan = outerPlanState(winstate);
1244 int numfuncs = winstate->numfuncs;
1245
1246 winstate->partition_spooled = false;
1247 winstate->framehead_valid = false;
1248 winstate->frametail_valid = false;
1249 winstate->grouptail_valid = false;
1250 winstate->spooled_rows = 0;
1251 winstate->currentpos = 0;
1252 winstate->frameheadpos = 0;
1253 winstate->frametailpos = 0;
1254 winstate->currentgroup = 0;
1255 winstate->frameheadgroup = 0;
1256 winstate->frametailgroup = 0;
1257 winstate->groupheadpos = 0;
1258 winstate->grouptailpos = -1; /* see update_grouptailpos */
1259 ExecClearTuple(winstate->agg_row_slot);
1260 if (winstate->framehead_slot)
1261 ExecClearTuple(winstate->framehead_slot);
1262 if (winstate->frametail_slot)
1263 ExecClearTuple(winstate->frametail_slot);
1264
1265 /*
1266 * If this is the very first partition, we need to fetch the first input
1267 * row to store in first_part_slot.
1268 */
1269 if (TupIsNull(winstate->first_part_slot))
1270 {
1272
1273 if (!TupIsNull(outerslot))
1275 else
1276 {
1277 /* outer plan is empty, so we have nothing to do */
1278 winstate->partition_spooled = true;
1279 winstate->more_partitions = false;
1280 return;
1281 }
1282 }
1283
1284 /* Create new tuplestore if not done already. */
1285 if (unlikely(winstate->buffer == NULL))
1286 prepare_tuplestore(winstate);
1287
1288 winstate->next_partition = false;
1289
1290 if (winstate->numaggs > 0)
1291 {
1292 WindowObject agg_winobj = winstate->agg_winobj;
1293
1294 /* reset mark and see positions for aggregate functions */
1295 agg_winobj->markpos = -1;
1296 agg_winobj->seekpos = -1;
1297
1298 /* Also reset the row counters for aggregates */
1299 winstate->aggregatedbase = 0;
1300 winstate->aggregatedupto = 0;
1301 }
1302
1303 /* reset mark and seek positions for each real window function */
1304 for (int i = 0; i < numfuncs; i++)
1305 {
1306 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1307
1308 if (!perfuncstate->plain_agg)
1309 {
1310 WindowObject winobj = perfuncstate->winobj;
1311
1312 winobj->markpos = -1;
1313 winobj->seekpos = -1;
1314
1315 /* reset null map */
1316 if (winobj->ignore_nulls == IGNORE_NULLS ||
1318 {
1319 int numargs = perfuncstate->numArguments;
1320
1321 for (int j = 0; j < numargs; j++)
1322 {
1323 int n = winobj->num_notnull_info[j];
1324
1325 if (n > 0)
1326 memset(winobj->notnull_info[j], 0,
1327 NN_POS_TO_BYTES(n));
1328 }
1329 }
1330 }
1331 }
1332
1333 /*
1334 * Store the first tuple into the tuplestore (it's always available now;
1335 * we either read it above, or saved it at the end of previous partition)
1336 */
1337 tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1338 winstate->spooled_rows++;
1339}
1340
1341/*
1342 * Read tuples from the outer node, up to and including position 'pos', and
1343 * store them into the tuplestore. If pos is -1, reads the whole partition.
1344 */
1345static void
1347{
1348 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1351 MemoryContext oldcontext;
1352
1353 if (!winstate->buffer)
1354 return; /* just a safety check */
1355 if (winstate->partition_spooled)
1356 return; /* whole partition done already */
1357
1358 /*
1359 * When in pass-through mode we can just exhaust all tuples in the current
1360 * partition. We don't need these tuples for any further window function
1361 * evaluation, however, we do need to keep them around if we're not the
1362 * top-level window as another WindowAgg node above must see these.
1363 */
1364 if (winstate->status != WINDOWAGG_RUN)
1365 {
1366 Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1368
1369 pos = -1;
1370 }
1371
1372 /*
1373 * If the tuplestore has spilled to disk, alternate reading and writing
1374 * becomes quite expensive due to frequent buffer flushes. It's cheaper
1375 * to force the entire partition to get spooled in one go.
1376 *
1377 * XXX this is a horrid kluge --- it'd be better to fix the performance
1378 * problem inside tuplestore. FIXME
1379 */
1380 else if (!tuplestore_in_memory(winstate->buffer))
1381 pos = -1;
1382
1383 outerPlan = outerPlanState(winstate);
1384
1385 /* Must be in query context to call outerplan */
1387
1388 while (winstate->spooled_rows <= pos || pos == -1)
1389 {
1391 if (TupIsNull(outerslot))
1392 {
1393 /* reached the end of the last partition */
1394 winstate->partition_spooled = true;
1395 winstate->more_partitions = false;
1396 break;
1397 }
1398
1399 if (node->partNumCols > 0)
1400 {
1401 ExprContext *econtext = winstate->tmpcontext;
1402
1403 econtext->ecxt_innertuple = winstate->first_part_slot;
1404 econtext->ecxt_outertuple = outerslot;
1405
1406 /* Check if this tuple still belongs to the current partition */
1407 if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1408 {
1409 /*
1410 * end of partition; copy the tuple for the next cycle.
1411 */
1413 winstate->partition_spooled = true;
1414 winstate->more_partitions = true;
1415 break;
1416 }
1417 }
1418
1419 /*
1420 * Remember the tuple unless we're the top-level window and we're in
1421 * pass-through mode.
1422 */
1423 if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1424 {
1425 /* Still in partition, so save it into the tuplestore */
1427 winstate->spooled_rows++;
1428 }
1429 }
1430
1431 MemoryContextSwitchTo(oldcontext);
1432}
1433
1434/*
1435 * release_partition
1436 * clear information kept within a partition, including
1437 * tuplestore and aggregate results.
1438 */
1439static void
1441{
1442 int i;
1443
1444 for (i = 0; i < winstate->numfuncs; i++)
1445 {
1446 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1447
1448 /* Release any partition-local state of this window function */
1449 if (perfuncstate->winobj)
1451 }
1452
1453 /*
1454 * Release all partition-local memory (in particular, any partition-local
1455 * state that we might have trashed our pointers to in the above loop, and
1456 * any aggregate temp data). We don't rely on retail pfree because some
1457 * aggregates might have allocated data we don't have direct pointers to.
1458 */
1460 MemoryContextReset(winstate->aggcontext);
1461 for (i = 0; i < winstate->numaggs; i++)
1462 {
1463 if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1465 }
1466
1467 if (winstate->buffer)
1468 tuplestore_clear(winstate->buffer);
1469 winstate->partition_spooled = false;
1470 winstate->next_partition = true;
1471}
1472
1473/*
1474 * row_is_in_frame
1475 * Determine whether a row is in the current row's window frame according
1476 * to our window framing rule
1477 *
1478 * The caller must have already determined that the row is in the partition
1479 * and fetched it into a slot if fetch_tuple is false.
1480 * This function just encapsulates the framing rules.
1481 *
1482 * Returns:
1483 * -1, if the row is out of frame and no succeeding rows can be in frame
1484 * 0, if the row is out of frame but succeeding rows might be in frame
1485 * 1, if the row is in frame
1486 *
1487 * May clobber winstate->temp_slot_2.
1488 */
1489static int
1491 bool fetch_tuple)
1492{
1493 WindowAggState *winstate = winobj->winstate;
1494 int frameOptions = winstate->frameOptions;
1495
1496 Assert(pos >= 0); /* else caller error */
1497
1498 /*
1499 * First, check frame starting conditions. We might as well delegate this
1500 * to update_frameheadpos always; it doesn't add any notable cost.
1501 */
1502 update_frameheadpos(winstate);
1503 if (pos < winstate->frameheadpos)
1504 return 0;
1505
1506 /*
1507 * Okay so far, now check frame ending conditions. Here, we avoid calling
1508 * update_frametailpos in simple cases, so as not to spool tuples further
1509 * ahead than necessary.
1510 */
1511 if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1512 {
1513 if (frameOptions & FRAMEOPTION_ROWS)
1514 {
1515 /* rows after current row are out of frame */
1516 if (pos > winstate->currentpos)
1517 return -1;
1518 }
1519 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1520 {
1521 /* following row that is not peer is out of frame */
1522 if (pos > winstate->currentpos)
1523 {
1524 if (fetch_tuple) /* need to fetch tuple? */
1525 if (!window_gettupleslot(winobj, pos, slot))
1526 return -1;
1527 if (!are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1528 return -1;
1529 }
1530 }
1531 else
1532 Assert(false);
1533 }
1534 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1535 {
1536 if (frameOptions & FRAMEOPTION_ROWS)
1537 {
1538 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1539 int64 frameendpos = 0;
1540
1541 /* rows after current row + offset are out of frame */
1542 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1543 offset = -offset;
1544
1545 /*
1546 * If we have an overflow, it means the frame end is beyond the
1547 * range of int64. Since currentpos >= 0, this can only be a
1548 * positive overflow. We treat this as meaning that the frame
1549 * extends to end of partition.
1550 */
1551 if (!pg_add_s64_overflow(winstate->currentpos, offset,
1552 &frameendpos) &&
1553 pos > frameendpos)
1554 return -1;
1555 }
1556 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1557 {
1558 /* hard cases, so delegate to update_frametailpos */
1559 update_frametailpos(winstate);
1560 if (pos >= winstate->frametailpos)
1561 return -1;
1562 }
1563 else
1564 Assert(false);
1565 }
1566
1567 /* Check exclusion clause */
1568 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1569 {
1570 if (pos == winstate->currentpos)
1571 return 0;
1572 }
1573 else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1574 ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1575 pos != winstate->currentpos))
1576 {
1577 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1578
1579 /* If no ORDER BY, all rows are peers with each other */
1580 if (node->ordNumCols == 0)
1581 return 0;
1582 /* Otherwise, check the group boundaries */
1583 if (pos >= winstate->groupheadpos)
1584 {
1585 update_grouptailpos(winstate);
1586 if (pos < winstate->grouptailpos)
1587 return 0;
1588 }
1589 }
1590
1591 /* If we get here, it's in frame */
1592 return 1;
1593}
1594
1595/*
1596 * update_frameheadpos
1597 * make frameheadpos valid for the current row
1598 *
1599 * Note that frameheadpos is computed without regard for any window exclusion
1600 * clause; the current row and/or its peers are considered part of the frame
1601 * for this purpose even if they must be excluded later.
1602 *
1603 * May clobber winstate->temp_slot_2.
1604 */
1605static void
1607{
1608 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1609 int frameOptions = winstate->frameOptions;
1610 MemoryContext oldcontext;
1611
1612 if (winstate->framehead_valid)
1613 return; /* already known for current row */
1614
1615 /* We may be called in a short-lived context */
1617
1618 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1619 {
1620 /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1621 winstate->frameheadpos = 0;
1622 winstate->framehead_valid = true;
1623 }
1624 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1625 {
1626 if (frameOptions & FRAMEOPTION_ROWS)
1627 {
1628 /* In ROWS mode, frame head is the same as current */
1629 winstate->frameheadpos = winstate->currentpos;
1630 winstate->framehead_valid = true;
1631 }
1632 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1633 {
1634 /* If no ORDER BY, all rows are peers with each other */
1635 if (node->ordNumCols == 0)
1636 {
1637 winstate->frameheadpos = 0;
1638 winstate->framehead_valid = true;
1639 MemoryContextSwitchTo(oldcontext);
1640 return;
1641 }
1642
1643 /*
1644 * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1645 * first row that is a peer of current row. We keep a copy of the
1646 * last-known frame head row in framehead_slot, and advance as
1647 * necessary. Note that if we reach end of partition, we will
1648 * leave frameheadpos = end+1 and framehead_slot empty.
1649 */
1651 winstate->framehead_ptr);
1652 if (winstate->frameheadpos == 0 &&
1653 TupIsNull(winstate->framehead_slot))
1654 {
1655 /* fetch first row into framehead_slot, if we didn't already */
1656 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1657 winstate->framehead_slot))
1658 elog(ERROR, "unexpected end of tuplestore");
1659 }
1660
1661 while (!TupIsNull(winstate->framehead_slot))
1662 {
1663 if (are_peers(winstate, winstate->framehead_slot,
1664 winstate->ss.ss_ScanTupleSlot))
1665 break; /* this row is the correct frame head */
1666 /* Note we advance frameheadpos even if the fetch fails */
1667 winstate->frameheadpos++;
1668 spool_tuples(winstate, winstate->frameheadpos);
1669 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1670 winstate->framehead_slot))
1671 break; /* end of partition */
1672 }
1673 winstate->framehead_valid = true;
1674 }
1675 else
1676 Assert(false);
1677 }
1678 else if (frameOptions & FRAMEOPTION_START_OFFSET)
1679 {
1680 if (frameOptions & FRAMEOPTION_ROWS)
1681 {
1682 /* In ROWS mode, bound is physically n before/after current */
1683 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1684
1685 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1686 offset = -offset;
1687
1688 /*
1689 * If we have an overflow, it means the frame head is beyond the
1690 * range of int64. Since currentpos >= 0, this can only be a
1691 * positive overflow. We treat this as being beyond end of
1692 * partition.
1693 */
1694 if (pg_add_s64_overflow(winstate->currentpos, offset,
1695 &winstate->frameheadpos))
1696 winstate->frameheadpos = PG_INT64_MAX;
1697
1698 /* frame head can't go before first row */
1699 if (winstate->frameheadpos < 0)
1700 winstate->frameheadpos = 0;
1701 else if (winstate->frameheadpos > winstate->currentpos + 1)
1702 {
1703 /* make sure frameheadpos is not past end of partition */
1704 spool_tuples(winstate, winstate->frameheadpos - 1);
1705 if (winstate->frameheadpos > winstate->spooled_rows)
1706 winstate->frameheadpos = winstate->spooled_rows;
1707 }
1708 winstate->framehead_valid = true;
1709 }
1710 else if (frameOptions & FRAMEOPTION_RANGE)
1711 {
1712 /*
1713 * In RANGE START_OFFSET mode, frame head is the first row that
1714 * satisfies the in_range constraint relative to the current row.
1715 * We keep a copy of the last-known frame head row in
1716 * framehead_slot, and advance as necessary. Note that if we
1717 * reach end of partition, we will leave frameheadpos = end+1 and
1718 * framehead_slot empty.
1719 */
1720 int sortCol = node->ordColIdx[0];
1721 bool sub,
1722 less;
1723
1724 /* We must have an ordering column */
1725 Assert(node->ordNumCols == 1);
1726
1727 /* Precompute flags for in_range checks */
1728 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1729 sub = true; /* subtract startOffset from current row */
1730 else
1731 sub = false; /* add it */
1732 less = false; /* normally, we want frame head >= sum */
1733 /* If sort order is descending, flip both flags */
1734 if (!winstate->inRangeAsc)
1735 {
1736 sub = !sub;
1737 less = true;
1738 }
1739
1741 winstate->framehead_ptr);
1742 if (winstate->frameheadpos == 0 &&
1743 TupIsNull(winstate->framehead_slot))
1744 {
1745 /* fetch first row into framehead_slot, if we didn't already */
1746 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1747 winstate->framehead_slot))
1748 elog(ERROR, "unexpected end of tuplestore");
1749 }
1750
1751 while (!TupIsNull(winstate->framehead_slot))
1752 {
1753 Datum headval,
1754 currval;
1755 bool headisnull,
1756 currisnull;
1757
1759 &headisnull);
1761 &currisnull);
1762 if (headisnull || currisnull)
1763 {
1764 /* order of the rows depends only on nulls_first */
1765 if (winstate->inRangeNullsFirst)
1766 {
1767 /* advance head if head is null and curr is not */
1768 if (!headisnull || currisnull)
1769 break;
1770 }
1771 else
1772 {
1773 /* advance head if head is not null and curr is null */
1774 if (headisnull || !currisnull)
1775 break;
1776 }
1777 }
1778 else
1779 {
1781 winstate->inRangeColl,
1782 headval,
1783 currval,
1784 winstate->startOffsetValue,
1785 BoolGetDatum(sub),
1786 BoolGetDatum(less))))
1787 break; /* this row is the correct frame head */
1788 }
1789 /* Note we advance frameheadpos even if the fetch fails */
1790 winstate->frameheadpos++;
1791 spool_tuples(winstate, winstate->frameheadpos);
1792 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1793 winstate->framehead_slot))
1794 break; /* end of partition */
1795 }
1796 winstate->framehead_valid = true;
1797 }
1798 else if (frameOptions & FRAMEOPTION_GROUPS)
1799 {
1800 /*
1801 * In GROUPS START_OFFSET mode, frame head is the first row of the
1802 * first peer group whose number satisfies the offset constraint.
1803 * We keep a copy of the last-known frame head row in
1804 * framehead_slot, and advance as necessary. Note that if we
1805 * reach end of partition, we will leave frameheadpos = end+1 and
1806 * framehead_slot empty.
1807 */
1808 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1809 int64 minheadgroup = 0;
1810
1811 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1812 minheadgroup = winstate->currentgroup - offset;
1813 else
1814 {
1815 /*
1816 * If we have an overflow, it means the target group is beyond
1817 * the range of int64. We treat this as "infinity", which
1818 * ensures the loop below advances to end of partition.
1819 */
1820 if (pg_add_s64_overflow(winstate->currentgroup, offset,
1821 &minheadgroup))
1823 }
1824
1826 winstate->framehead_ptr);
1827 if (winstate->frameheadpos == 0 &&
1828 TupIsNull(winstate->framehead_slot))
1829 {
1830 /* fetch first row into framehead_slot, if we didn't already */
1831 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1832 winstate->framehead_slot))
1833 elog(ERROR, "unexpected end of tuplestore");
1834 }
1835
1836 while (!TupIsNull(winstate->framehead_slot))
1837 {
1838 if (winstate->frameheadgroup >= minheadgroup)
1839 break; /* this row is the correct frame head */
1840 ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1841 /* Note we advance frameheadpos even if the fetch fails */
1842 winstate->frameheadpos++;
1843 spool_tuples(winstate, winstate->frameheadpos);
1844 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1845 winstate->framehead_slot))
1846 break; /* end of partition */
1847 if (!are_peers(winstate, winstate->temp_slot_2,
1848 winstate->framehead_slot))
1849 winstate->frameheadgroup++;
1850 }
1851 ExecClearTuple(winstate->temp_slot_2);
1852 winstate->framehead_valid = true;
1853 }
1854 else
1855 Assert(false);
1856 }
1857 else
1858 Assert(false);
1859
1860 MemoryContextSwitchTo(oldcontext);
1861}
1862
1863/*
1864 * update_frametailpos
1865 * make frametailpos valid for the current row
1866 *
1867 * Note that frametailpos is computed without regard for any window exclusion
1868 * clause; the current row and/or its peers are considered part of the frame
1869 * for this purpose even if they must be excluded later.
1870 *
1871 * May clobber winstate->temp_slot_2.
1872 */
1873static void
1875{
1876 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1877 int frameOptions = winstate->frameOptions;
1878 MemoryContext oldcontext;
1879
1880 if (winstate->frametail_valid)
1881 return; /* already known for current row */
1882
1883 /* We may be called in a short-lived context */
1885
1886 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1887 {
1888 /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1889 spool_tuples(winstate, -1);
1890 winstate->frametailpos = winstate->spooled_rows;
1891 winstate->frametail_valid = true;
1892 }
1893 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1894 {
1895 if (frameOptions & FRAMEOPTION_ROWS)
1896 {
1897 /* In ROWS mode, exactly the rows up to current are in frame */
1898 winstate->frametailpos = winstate->currentpos + 1;
1899 winstate->frametail_valid = true;
1900 }
1901 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1902 {
1903 /* If no ORDER BY, all rows are peers with each other */
1904 if (node->ordNumCols == 0)
1905 {
1906 spool_tuples(winstate, -1);
1907 winstate->frametailpos = winstate->spooled_rows;
1908 winstate->frametail_valid = true;
1909 MemoryContextSwitchTo(oldcontext);
1910 return;
1911 }
1912
1913 /*
1914 * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1915 * row that is a peer of current row, frame tail is the row after
1916 * that (if any). We keep a copy of the last-known frame tail row
1917 * in frametail_slot, and advance as necessary. Note that if we
1918 * reach end of partition, we will leave frametailpos = end+1 and
1919 * frametail_slot empty.
1920 */
1922 winstate->frametail_ptr);
1923 if (winstate->frametailpos == 0 &&
1924 TupIsNull(winstate->frametail_slot))
1925 {
1926 /* fetch first row into frametail_slot, if we didn't already */
1927 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1928 winstate->frametail_slot))
1929 elog(ERROR, "unexpected end of tuplestore");
1930 }
1931
1932 while (!TupIsNull(winstate->frametail_slot))
1933 {
1934 if (winstate->frametailpos > winstate->currentpos &&
1935 !are_peers(winstate, winstate->frametail_slot,
1936 winstate->ss.ss_ScanTupleSlot))
1937 break; /* this row is the frame tail */
1938 /* Note we advance frametailpos even if the fetch fails */
1939 winstate->frametailpos++;
1940 spool_tuples(winstate, winstate->frametailpos);
1941 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1942 winstate->frametail_slot))
1943 break; /* end of partition */
1944 }
1945 winstate->frametail_valid = true;
1946 }
1947 else
1948 Assert(false);
1949 }
1950 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1951 {
1952 if (frameOptions & FRAMEOPTION_ROWS)
1953 {
1954 /* In ROWS mode, bound is physically n before/after current */
1955 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1956
1957 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1958 offset = -offset;
1959
1960 /*
1961 * If we have an overflow, it means the frame tail is beyond the
1962 * range of int64. Since currentpos >= 0, this can only be a
1963 * positive overflow. We treat this as being beyond end of
1964 * partition.
1965 */
1966 if (pg_add_s64_overflow(winstate->currentpos, offset,
1967 &winstate->frametailpos) ||
1968 pg_add_s64_overflow(winstate->frametailpos, 1,
1969 &winstate->frametailpos))
1970 winstate->frametailpos = PG_INT64_MAX;
1971
1972 /* smallest allowable value of frametailpos is 0 */
1973 if (winstate->frametailpos < 0)
1974 winstate->frametailpos = 0;
1975 else if (winstate->frametailpos > winstate->currentpos + 1)
1976 {
1977 /* make sure frametailpos is not past end of partition */
1978 spool_tuples(winstate, winstate->frametailpos - 1);
1979 if (winstate->frametailpos > winstate->spooled_rows)
1980 winstate->frametailpos = winstate->spooled_rows;
1981 }
1982 winstate->frametail_valid = true;
1983 }
1984 else if (frameOptions & FRAMEOPTION_RANGE)
1985 {
1986 /*
1987 * In RANGE END_OFFSET mode, frame end is the last row that
1988 * satisfies the in_range constraint relative to the current row,
1989 * frame tail is the row after that (if any). We keep a copy of
1990 * the last-known frame tail row in frametail_slot, and advance as
1991 * necessary. Note that if we reach end of partition, we will
1992 * leave frametailpos = end+1 and frametail_slot empty.
1993 */
1994 int sortCol = node->ordColIdx[0];
1995 bool sub,
1996 less;
1997
1998 /* We must have an ordering column */
1999 Assert(node->ordNumCols == 1);
2000
2001 /* Precompute flags for in_range checks */
2002 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
2003 sub = true; /* subtract endOffset from current row */
2004 else
2005 sub = false; /* add it */
2006 less = true; /* normally, we want frame tail <= sum */
2007 /* If sort order is descending, flip both flags */
2008 if (!winstate->inRangeAsc)
2009 {
2010 sub = !sub;
2011 less = false;
2012 }
2013
2015 winstate->frametail_ptr);
2016 if (winstate->frametailpos == 0 &&
2017 TupIsNull(winstate->frametail_slot))
2018 {
2019 /* fetch first row into frametail_slot, if we didn't already */
2020 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2021 winstate->frametail_slot))
2022 elog(ERROR, "unexpected end of tuplestore");
2023 }
2024
2025 while (!TupIsNull(winstate->frametail_slot))
2026 {
2027 Datum tailval,
2028 currval;
2029 bool tailisnull,
2030 currisnull;
2031
2033 &tailisnull);
2035 &currisnull);
2036 if (tailisnull || currisnull)
2037 {
2038 /* order of the rows depends only on nulls_first */
2039 if (winstate->inRangeNullsFirst)
2040 {
2041 /* advance tail if tail is null or curr is not */
2042 if (!tailisnull)
2043 break;
2044 }
2045 else
2046 {
2047 /* advance tail if tail is not null or curr is null */
2048 if (!currisnull)
2049 break;
2050 }
2051 }
2052 else
2053 {
2055 winstate->inRangeColl,
2056 tailval,
2057 currval,
2058 winstate->endOffsetValue,
2059 BoolGetDatum(sub),
2060 BoolGetDatum(less))))
2061 break; /* this row is the correct frame tail */
2062 }
2063 /* Note we advance frametailpos even if the fetch fails */
2064 winstate->frametailpos++;
2065 spool_tuples(winstate, winstate->frametailpos);
2066 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2067 winstate->frametail_slot))
2068 break; /* end of partition */
2069 }
2070 winstate->frametail_valid = true;
2071 }
2072 else if (frameOptions & FRAMEOPTION_GROUPS)
2073 {
2074 /*
2075 * In GROUPS END_OFFSET mode, frame end is the last row of the
2076 * last peer group whose number satisfies the offset constraint,
2077 * and frame tail is the row after that (if any). We keep a copy
2078 * of the last-known frame tail row in frametail_slot, and advance
2079 * as necessary. Note that if we reach end of partition, we will
2080 * leave frametailpos = end+1 and frametail_slot empty.
2081 */
2082 int64 offset = DatumGetInt64(winstate->endOffsetValue);
2083 int64 maxtailgroup = 0;
2084
2085 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
2086 maxtailgroup = winstate->currentgroup - offset;
2087 else
2088 {
2089 /*
2090 * If we have an overflow, it means the target group is beyond
2091 * the range of int64. We treat this as "infinity", which
2092 * ensures the loop below advances to end of partition.
2093 */
2094 if (pg_add_s64_overflow(winstate->currentgroup, offset,
2095 &maxtailgroup))
2097 }
2098
2100 winstate->frametail_ptr);
2101 if (winstate->frametailpos == 0 &&
2102 TupIsNull(winstate->frametail_slot))
2103 {
2104 /* fetch first row into frametail_slot, if we didn't already */
2105 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2106 winstate->frametail_slot))
2107 elog(ERROR, "unexpected end of tuplestore");
2108 }
2109
2110 while (!TupIsNull(winstate->frametail_slot))
2111 {
2112 if (winstate->frametailgroup > maxtailgroup)
2113 break; /* this row is the correct frame tail */
2114 ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
2115 /* Note we advance frametailpos even if the fetch fails */
2116 winstate->frametailpos++;
2117 spool_tuples(winstate, winstate->frametailpos);
2118 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2119 winstate->frametail_slot))
2120 break; /* end of partition */
2121 if (!are_peers(winstate, winstate->temp_slot_2,
2122 winstate->frametail_slot))
2123 winstate->frametailgroup++;
2124 }
2125 ExecClearTuple(winstate->temp_slot_2);
2126 winstate->frametail_valid = true;
2127 }
2128 else
2129 Assert(false);
2130 }
2131 else
2132 Assert(false);
2133
2134 MemoryContextSwitchTo(oldcontext);
2135}
2136
2137/*
2138 * update_grouptailpos
2139 * make grouptailpos valid for the current row
2140 *
2141 * May clobber winstate->temp_slot_2.
2142 */
2143static void
2145{
2146 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2147 MemoryContext oldcontext;
2148
2149 if (winstate->grouptail_valid)
2150 return; /* already known for current row */
2151
2152 /* We may be called in a short-lived context */
2154
2155 /* If no ORDER BY, all rows are peers with each other */
2156 if (node->ordNumCols == 0)
2157 {
2158 spool_tuples(winstate, -1);
2159 winstate->grouptailpos = winstate->spooled_rows;
2160 winstate->grouptail_valid = true;
2161 MemoryContextSwitchTo(oldcontext);
2162 return;
2163 }
2164
2165 /*
2166 * Because grouptail_valid is reset only when current row advances into a
2167 * new peer group, we always reach here knowing that grouptailpos needs to
2168 * be advanced by at least one row. Hence, unlike the otherwise similar
2169 * case for frame tail tracking, we do not need persistent storage of the
2170 * group tail row.
2171 */
2172 Assert(winstate->grouptailpos <= winstate->currentpos);
2174 winstate->grouptail_ptr);
2175 for (;;)
2176 {
2177 /* Note we advance grouptailpos even if the fetch fails */
2178 winstate->grouptailpos++;
2179 spool_tuples(winstate, winstate->grouptailpos);
2180 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2181 winstate->temp_slot_2))
2182 break; /* end of partition */
2183 if (winstate->grouptailpos > winstate->currentpos &&
2184 !are_peers(winstate, winstate->temp_slot_2,
2185 winstate->ss.ss_ScanTupleSlot))
2186 break; /* this row is the group tail */
2187 }
2188 ExecClearTuple(winstate->temp_slot_2);
2189 winstate->grouptail_valid = true;
2190
2191 MemoryContextSwitchTo(oldcontext);
2192}
2193
2194/*
2195 * calculate_frame_offsets
2196 * Determine the startOffsetValue and endOffsetValue values for the
2197 * WindowAgg's frame options.
2198 */
2199static pg_noinline void
2201{
2202 WindowAggState *winstate = castNode(WindowAggState, pstate);
2203 ExprContext *econtext;
2204 int frameOptions = winstate->frameOptions;
2205 Datum value;
2206 bool isnull;
2207 int16 len;
2208 bool byval;
2209
2210 /* Ensure we've not been called before for this scan */
2211 Assert(winstate->all_first);
2212
2213 econtext = winstate->ss.ps.ps_ExprContext;
2214
2215 if (frameOptions & FRAMEOPTION_START_OFFSET)
2216 {
2217 Assert(winstate->startOffset != NULL);
2219 econtext,
2220 &isnull);
2221 if (isnull)
2222 ereport(ERROR,
2224 errmsg("frame starting offset must not be null")));
2225 /* copy value into query-lifespan context */
2227 &len,
2228 &byval);
2229 winstate->startOffsetValue = datumCopy(value, byval, len);
2230 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2231 {
2232 /* value is known to be int8 */
2233 int64 offset = DatumGetInt64(value);
2234
2235 if (offset < 0)
2236 ereport(ERROR,
2238 errmsg("frame starting offset must not be negative")));
2239 }
2240 }
2241
2242 if (frameOptions & FRAMEOPTION_END_OFFSET)
2243 {
2244 Assert(winstate->endOffset != NULL);
2246 econtext,
2247 &isnull);
2248 if (isnull)
2249 ereport(ERROR,
2251 errmsg("frame ending offset must not be null")));
2252 /* copy value into query-lifespan context */
2253 get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2254 &len,
2255 &byval);
2256 winstate->endOffsetValue = datumCopy(value, byval, len);
2257 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2258 {
2259 /* value is known to be int8 */
2260 int64 offset = DatumGetInt64(value);
2261
2262 if (offset < 0)
2263 ereport(ERROR,
2265 errmsg("frame ending offset must not be negative")));
2266 }
2267 }
2268 winstate->all_first = false;
2269}
2270
2271/* -----------------
2272 * ExecWindowAgg
2273 *
2274 * ExecWindowAgg receives tuples from its outer subplan and
2275 * stores them into a tuplestore, then processes window functions.
2276 * This node doesn't reduce nor qualify any row so the number of
2277 * returned rows is exactly the same as its outer subplan's result.
2278 * -----------------
2279 */
2280static TupleTableSlot *
2282{
2283 WindowAggState *winstate = castNode(WindowAggState, pstate);
2284 TupleTableSlot *slot;
2285 ExprContext *econtext;
2286 int i;
2287 int numfuncs;
2288
2290
2291 if (winstate->status == WINDOWAGG_DONE)
2292 return NULL;
2293
2294 /*
2295 * Compute frame offset values, if any, during first call (or after a
2296 * rescan). These are assumed to hold constant throughout the scan; if
2297 * user gives us a volatile expression, we'll only use its initial value.
2298 */
2299 if (unlikely(winstate->all_first))
2301
2302 /* We need to loop as the runCondition or qual may filter out tuples */
2303 for (;;)
2304 {
2305 if (winstate->next_partition)
2306 {
2307 /* Initialize for first partition and set current row = 0 */
2308 begin_partition(winstate);
2309 /* If there are no input rows, we'll detect that and exit below */
2310 }
2311 else
2312 {
2313 /* Advance current row within partition */
2314 winstate->currentpos++;
2315 /* This might mean that the frame moves, too */
2316 winstate->framehead_valid = false;
2317 winstate->frametail_valid = false;
2318 /* we don't need to invalidate grouptail here; see below */
2319 }
2320
2321 /*
2322 * Spool all tuples up to and including the current row, if we haven't
2323 * already
2324 */
2325 spool_tuples(winstate, winstate->currentpos);
2326
2327 /* Move to the next partition if we reached the end of this partition */
2328 if (winstate->partition_spooled &&
2329 winstate->currentpos >= winstate->spooled_rows)
2330 {
2331 release_partition(winstate);
2332
2333 if (winstate->more_partitions)
2334 {
2335 begin_partition(winstate);
2336 Assert(winstate->spooled_rows > 0);
2337
2338 /* Come out of pass-through mode when changing partition */
2339 winstate->status = WINDOWAGG_RUN;
2340 }
2341 else
2342 {
2343 /* No further partitions? We're done */
2344 winstate->status = WINDOWAGG_DONE;
2345 return NULL;
2346 }
2347 }
2348
2349 /* final output execution is in ps_ExprContext */
2350 econtext = winstate->ss.ps.ps_ExprContext;
2351
2352 /* Clear the per-output-tuple context for current row */
2353 ResetExprContext(econtext);
2354
2355 /*
2356 * Read the current row from the tuplestore, and save in
2357 * ScanTupleSlot. (We can't rely on the outerplan's output slot
2358 * because we may have to read beyond the current row. Also, we have
2359 * to actually copy the row out of the tuplestore, since window
2360 * function evaluation might cause the tuplestore to dump its state to
2361 * disk.)
2362 *
2363 * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2364 * we must also detect entering a new peer group and update associated
2365 * state when that happens. We use temp_slot_2 to temporarily hold
2366 * the previous row for this purpose.
2367 *
2368 * Current row must be in the tuplestore, since we spooled it above.
2369 */
2371 if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2374 winstate->currentpos > 0)
2375 {
2376 ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2377 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2378 winstate->ss.ss_ScanTupleSlot))
2379 elog(ERROR, "unexpected end of tuplestore");
2380 if (!are_peers(winstate, winstate->temp_slot_2,
2381 winstate->ss.ss_ScanTupleSlot))
2382 {
2383 winstate->currentgroup++;
2384 winstate->groupheadpos = winstate->currentpos;
2385 winstate->grouptail_valid = false;
2386 }
2387 ExecClearTuple(winstate->temp_slot_2);
2388 }
2389 else
2390 {
2391 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2392 winstate->ss.ss_ScanTupleSlot))
2393 elog(ERROR, "unexpected end of tuplestore");
2394 }
2395
2396 /* don't evaluate the window functions when we're in pass-through mode */
2397 if (winstate->status == WINDOWAGG_RUN)
2398 {
2399 /*
2400 * Evaluate true window functions
2401 */
2402 numfuncs = winstate->numfuncs;
2403 for (i = 0; i < numfuncs; i++)
2404 {
2405 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2406
2407 if (perfuncstate->plain_agg)
2408 continue;
2410 &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2411 &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2412 }
2413
2414 /*
2415 * Evaluate aggregates
2416 */
2417 if (winstate->numaggs > 0)
2418 eval_windowaggregates(winstate);
2419 }
2420
2421 /*
2422 * If we have created auxiliary read pointers for the frame or group
2423 * boundaries, force them to be kept up-to-date, because we don't know
2424 * whether the window function(s) will do anything that requires that.
2425 * Failing to advance the pointers would result in being unable to
2426 * trim data from the tuplestore, which is bad. (If we could know in
2427 * advance whether the window functions will use frame boundary info,
2428 * we could skip creating these pointers in the first place ... but
2429 * unfortunately the window function API doesn't require that.)
2430 */
2431 if (winstate->framehead_ptr >= 0)
2432 update_frameheadpos(winstate);
2433 if (winstate->frametail_ptr >= 0)
2434 update_frametailpos(winstate);
2435 if (winstate->grouptail_ptr >= 0)
2436 update_grouptailpos(winstate);
2437
2438 /*
2439 * Truncate any no-longer-needed rows from the tuplestore.
2440 */
2441 tuplestore_trim(winstate->buffer);
2442
2443 /*
2444 * Form and return a projection tuple using the windowfunc results and
2445 * the current row. Setting ecxt_outertuple arranges that any Vars
2446 * will be evaluated with respect to that row.
2447 */
2448 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2449
2450 slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2451
2452 if (winstate->status == WINDOWAGG_RUN)
2453 {
2454 econtext->ecxt_scantuple = slot;
2455
2456 /*
2457 * Now evaluate the run condition to see if we need to go into
2458 * pass-through mode, or maybe stop completely.
2459 */
2460 if (!ExecQual(winstate->runcondition, econtext))
2461 {
2462 /*
2463 * Determine which mode to move into. If there is no
2464 * PARTITION BY clause and we're the top-level WindowAgg then
2465 * we're done. This tuple and any future tuples cannot
2466 * possibly match the runcondition. However, when there is a
2467 * PARTITION BY clause or we're not the top-level window we
2468 * can't just stop as we need to either process other
2469 * partitions or ensure WindowAgg nodes above us receive all
2470 * of the tuples they need to process their WindowFuncs.
2471 */
2472 if (winstate->use_pass_through)
2473 {
2474 /*
2475 * When switching into a pass-through mode, we'd better
2476 * NULLify the aggregate results as these are no longer
2477 * updated and NULLifying them avoids the old stale
2478 * results lingering. Some of these might be byref types
2479 * so we can't have them pointing to free'd memory. The
2480 * planner insisted that quals used in the runcondition
2481 * are strict, so the top-level WindowAgg will always
2482 * filter these NULLs out in the filter clause.
2483 */
2484 numfuncs = winstate->numfuncs;
2485 for (i = 0; i < numfuncs; i++)
2486 {
2487 econtext->ecxt_aggvalues[i] = (Datum) 0;
2488 econtext->ecxt_aggnulls[i] = true;
2489 }
2490
2491 /*
2492 * STRICT pass-through mode is required for the top window
2493 * when there is a PARTITION BY clause. Otherwise we must
2494 * ensure we store tuples that don't match the
2495 * runcondition so they're available to WindowAggs above.
2496 */
2497 if (winstate->top_window)
2498 {
2500 continue;
2501 }
2502 else
2503 {
2504 winstate->status = WINDOWAGG_PASSTHROUGH;
2505 }
2506 }
2507 else
2508 {
2509 /*
2510 * Pass-through not required. We can just return NULL.
2511 * Nothing else will match the runcondition.
2512 */
2513 winstate->status = WINDOWAGG_DONE;
2514 return NULL;
2515 }
2516 }
2517
2518 /*
2519 * Filter out any tuples we don't need in the top-level WindowAgg.
2520 */
2521 if (!ExecQual(winstate->ss.ps.qual, econtext))
2522 {
2523 InstrCountFiltered1(winstate, 1);
2524 continue;
2525 }
2526
2527 break;
2528 }
2529
2530 /*
2531 * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2532 * we're anything apart from the top window.
2533 */
2534 else if (!winstate->top_window)
2535 break;
2536 }
2537
2538 return slot;
2539}
2540
2541/* -----------------
2542 * ExecInitWindowAgg
2543 *
2544 * Creates the run-time information for the WindowAgg node produced by the
2545 * planner and initializes its outer subtree
2546 * -----------------
2547 */
2549ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
2550{
2551 WindowAggState *winstate;
2552 Plan *outerPlan;
2553 ExprContext *econtext;
2554 ExprContext *tmpcontext;
2555 WindowStatePerFunc perfunc;
2556 WindowStatePerAgg peragg;
2557 int frameOptions = node->frameOptions;
2558 int numfuncs,
2559 wfuncno,
2560 numaggs,
2561 aggno;
2563 ListCell *l;
2564
2565 /* check for unsupported flags */
2566 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2567
2568 /*
2569 * create state structure
2570 */
2571 winstate = makeNode(WindowAggState);
2572 winstate->ss.ps.plan = (Plan *) node;
2573 winstate->ss.ps.state = estate;
2574 winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2575
2576 /* copy frame options to state node for easy access */
2577 winstate->frameOptions = frameOptions;
2578
2579 /*
2580 * Create expression contexts. We need two, one for per-input-tuple
2581 * processing and one for per-output-tuple processing. We cheat a little
2582 * by using ExecAssignExprContext() to build both.
2583 */
2584 ExecAssignExprContext(estate, &winstate->ss.ps);
2585 tmpcontext = winstate->ss.ps.ps_ExprContext;
2586 winstate->tmpcontext = tmpcontext;
2587 ExecAssignExprContext(estate, &winstate->ss.ps);
2588
2589 /* Create long-lived context for storage of partition-local memory etc */
2590 winstate->partcontext =
2592 "WindowAgg Partition",
2594
2595 /*
2596 * Create mid-lived context for aggregate trans values etc.
2597 *
2598 * Note that moving aggregates each use their own private context, not
2599 * this one.
2600 */
2601 winstate->aggcontext =
2603 "WindowAgg Aggregates",
2605
2606 /* Only the top-level WindowAgg may have a qual */
2607 Assert(node->plan.qual == NIL || node->topWindow);
2608
2609 /* Initialize the qual */
2610 winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2611 (PlanState *) winstate);
2612
2613 /*
2614 * Setup the run condition, if we received one from the query planner.
2615 * When set, this may allow us to move into pass-through mode so that we
2616 * don't have to perform any further evaluation of WindowFuncs in the
2617 * current partition or possibly stop returning tuples altogether when all
2618 * tuples are in the same partition.
2619 */
2620 winstate->runcondition = ExecInitQual(node->runCondition,
2621 (PlanState *) winstate);
2622
2623 /*
2624 * When we're not the top-level WindowAgg node or we are but have a
2625 * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2626 * modes when the runCondition becomes false.
2627 */
2628 winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2629
2630 /* remember if we're the top-window or we are below the top-window */
2631 winstate->top_window = node->topWindow;
2632
2633 /*
2634 * initialize child nodes
2635 */
2636 outerPlan = outerPlan(node);
2637 outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2638
2639 /*
2640 * initialize source tuple type (which is also the tuple type that we'll
2641 * store in the tuplestore and use in all our working slots).
2642 */
2645
2646 /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2647 winstate->ss.ps.outeropsset = true;
2648 winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2649 winstate->ss.ps.outeropsfixed = true;
2650
2651 /*
2652 * tuple table initialization
2653 */
2656 winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2658 winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2660 winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2662
2663 /*
2664 * create frame head and tail slots only if needed (must create slots in
2665 * exactly the same cases that update_frameheadpos and update_frametailpos
2666 * need them)
2667 */
2668 winstate->framehead_slot = winstate->frametail_slot = NULL;
2669
2670 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2671 {
2672 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2673 node->ordNumCols != 0) ||
2674 (frameOptions & FRAMEOPTION_START_OFFSET))
2677 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2678 node->ordNumCols != 0) ||
2679 (frameOptions & FRAMEOPTION_END_OFFSET))
2682 }
2683
2684 /*
2685 * Initialize result slot, type and projection.
2686 */
2688 ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2689
2690 /* Set up data for comparing tuples */
2691 if (node->partNumCols > 0)
2692 winstate->partEqfunction =
2694 node->partNumCols,
2695 node->partColIdx,
2696 node->partOperators,
2697 node->partCollations,
2698 &winstate->ss.ps);
2699
2700 if (node->ordNumCols > 0)
2701 winstate->ordEqfunction =
2703 node->ordNumCols,
2704 node->ordColIdx,
2705 node->ordOperators,
2706 node->ordCollations,
2707 &winstate->ss.ps);
2708
2709 /*
2710 * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2711 */
2712 numfuncs = winstate->numfuncs;
2713 numaggs = winstate->numaggs;
2714 econtext = winstate->ss.ps.ps_ExprContext;
2715 econtext->ecxt_aggvalues = palloc0_array(Datum, numfuncs);
2716 econtext->ecxt_aggnulls = palloc0_array(bool, numfuncs);
2717
2718 /*
2719 * allocate per-wfunc/per-agg state information.
2720 */
2721 perfunc = palloc0_array(WindowStatePerFuncData, numfuncs);
2722 peragg = palloc0_array(WindowStatePerAggData, numaggs);
2723 winstate->perfunc = perfunc;
2724 winstate->peragg = peragg;
2725
2726 wfuncno = -1;
2727 aggno = -1;
2728 foreach(l, winstate->funcs)
2729 {
2730 WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2731 WindowFunc *wfunc = wfuncstate->wfunc;
2734 int i;
2735
2736 if (wfunc->winref != node->winref) /* planner screwed up? */
2737 elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2738 wfunc->winref, node->winref);
2739
2740 /*
2741 * Look for a previous duplicate window function, which needs the same
2742 * ignore_nulls value
2743 */
2744 for (i = 0; i <= wfuncno; i++)
2745 {
2746 if (equal(wfunc, perfunc[i].wfunc) &&
2747 !contain_volatile_functions((Node *) wfunc))
2748 break;
2749 }
2750 if (i <= wfuncno && wfunc->ignore_nulls == perfunc[i].ignore_nulls)
2751 {
2752 /* Found a match to an existing entry, so just mark it */
2753 wfuncstate->wfuncno = i;
2754 continue;
2755 }
2756
2757 /* Nope, so assign a new PerAgg record */
2758 perfuncstate = &perfunc[++wfuncno];
2759
2760 /* Mark WindowFunc state node with assigned index in the result array */
2761 wfuncstate->wfuncno = wfuncno;
2762
2763 /* Check permission to call window function */
2765 ACL_EXECUTE);
2766 if (aclresult != ACLCHECK_OK)
2768 get_func_name(wfunc->winfnoid));
2770
2771 /* Fill in the perfuncstate data */
2772 perfuncstate->wfuncstate = wfuncstate;
2773 perfuncstate->wfunc = wfunc;
2774 perfuncstate->numArguments = list_length(wfuncstate->args);
2775 perfuncstate->winCollation = wfunc->inputcollid;
2776
2777 get_typlenbyval(wfunc->wintype,
2778 &perfuncstate->resulttypeLen,
2779 &perfuncstate->resulttypeByVal);
2780
2781 /*
2782 * If it's really just a plain aggregate function, we'll emulate the
2783 * Agg environment for it.
2784 */
2785 perfuncstate->plain_agg = wfunc->winagg;
2786 if (wfunc->winagg)
2787 {
2789
2790 perfuncstate->aggno = ++aggno;
2791 peraggstate = &winstate->peragg[aggno];
2792 initialize_peragg(winstate, wfunc, peraggstate);
2793 peraggstate->wfuncno = wfuncno;
2794 }
2795 else
2796 {
2798
2799 winobj->winstate = winstate;
2800 winobj->argstates = wfuncstate->args;
2801 winobj->localmem = NULL;
2802 perfuncstate->winobj = winobj;
2803 winobj->ignore_nulls = wfunc->ignore_nulls;
2805
2806 /* It's a real window function, so set up to call it. */
2807 fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2808 econtext->ecxt_per_query_memory);
2809 fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2810 }
2811 }
2812
2813 /* Update numfuncs, numaggs to match number of unique functions found */
2814 winstate->numfuncs = wfuncno + 1;
2815 winstate->numaggs = aggno + 1;
2816
2817 /* Set up WindowObject for aggregates, if needed */
2818 if (winstate->numaggs > 0)
2819 {
2821
2822 agg_winobj->winstate = winstate;
2823 agg_winobj->argstates = NIL;
2824 agg_winobj->localmem = NULL;
2825 /* make sure markptr = -1 to invalidate. It may not get used */
2826 agg_winobj->markptr = -1;
2827 agg_winobj->readptr = -1;
2828 winstate->agg_winobj = agg_winobj;
2829 }
2830
2831 /* Set the status to running */
2832 winstate->status = WINDOWAGG_RUN;
2833
2834 /* initialize frame bound offset expressions */
2835 winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2836 (PlanState *) winstate);
2837 winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2838 (PlanState *) winstate);
2839
2840 /* Lookup in_range support functions if needed */
2841 if (OidIsValid(node->startInRangeFunc))
2842 fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2843 if (OidIsValid(node->endInRangeFunc))
2844 fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2845 winstate->inRangeColl = node->inRangeColl;
2846 winstate->inRangeAsc = node->inRangeAsc;
2847 winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2848
2849 winstate->all_first = true;
2850 winstate->partition_spooled = false;
2851 winstate->more_partitions = false;
2852 winstate->next_partition = true;
2853
2854 return winstate;
2855}
2856
2857/* -----------------
2858 * ExecEndWindowAgg
2859 * -----------------
2860 */
2861void
2863{
2865 int i;
2866
2867 if (node->buffer != NULL)
2868 {
2869 tuplestore_end(node->buffer);
2870
2871 /* nullify so that release_partition skips the tuplestore_clear() */
2872 node->buffer = NULL;
2873 }
2874
2875 release_partition(node);
2876
2877 for (i = 0; i < node->numaggs; i++)
2878 {
2879 if (node->peragg[i].aggcontext != node->aggcontext)
2881 }
2884
2885 pfree(node->perfunc);
2886 pfree(node->peragg);
2887
2888 outerPlan = outerPlanState(node);
2890}
2891
2892/* -----------------
2893 * ExecReScanWindowAgg
2894 * -----------------
2895 */
2896void
2898{
2900 ExprContext *econtext = node->ss.ps.ps_ExprContext;
2901
2902 node->status = WINDOWAGG_RUN;
2903 node->all_first = true;
2904
2905 /* release tuplestore et al */
2906 release_partition(node);
2907
2908 /* release all temp tuples, but especially first_part_slot */
2914 if (node->framehead_slot)
2916 if (node->frametail_slot)
2918
2919 /* Forget current wfunc values */
2920 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2921 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2922
2923 /*
2924 * if chgParam of subnode is not null then plan will be re-scanned by
2925 * first ExecProcNode.
2926 */
2927 if (outerPlan->chgParam == NULL)
2929}
2930
2931/*
2932 * initialize_peragg
2933 *
2934 * Almost same as in nodeAgg.c, except we don't support DISTINCT currently.
2935 */
2936static WindowStatePerAggData *
2939{
2941 int numArguments;
2944 Oid aggtranstype;
2947 bool use_ma_code;
2948 Oid transfn_oid,
2949 invtransfn_oid,
2950 finalfn_oid;
2951 bool finalextra;
2952 char finalmodify;
2955 *finalfnexpr;
2957 int i;
2958 ListCell *lc;
2959
2960 numArguments = list_length(wfunc->args);
2961
2962 i = 0;
2963 foreach(lc, wfunc->args)
2964 {
2965 inputTypes[i++] = exprType((Node *) lfirst(lc));
2966 }
2967
2970 elog(ERROR, "cache lookup failed for aggregate %u",
2971 wfunc->winfnoid);
2973
2974 /*
2975 * Figure out whether we want to use the moving-aggregate implementation,
2976 * and collect the right set of fields from the pg_aggregate entry.
2977 *
2978 * It's possible that an aggregate would supply a safe moving-aggregate
2979 * implementation and an unsafe normal one, in which case our hand is
2980 * forced. Otherwise, if the frame head can't move, we don't need
2981 * moving-aggregate code. Even if we'd like to use it, don't do so if the
2982 * aggregate's arguments (and FILTER clause if any) contain any calls to
2983 * volatile functions. Otherwise, the difference between restarting and
2984 * not restarting the aggregation would be user-visible.
2985 *
2986 * We also don't risk using moving aggregates when there are subplans in
2987 * the arguments or FILTER clause. This is partly because
2988 * contain_volatile_functions() doesn't look inside subplans; but there
2989 * are other reasons why a subplan's output might be volatile. For
2990 * example, syncscan mode can render the results nonrepeatable.
2991 */
2992 if (!OidIsValid(aggform->aggminvtransfn))
2993 use_ma_code = false; /* sine qua non */
2994 else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2995 aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2996 use_ma_code = true; /* decision forced by safety */
2998 use_ma_code = false; /* non-moving frame head */
2999 else if (contain_volatile_functions((Node *) wfunc))
3000 use_ma_code = false; /* avoid possible behavioral change */
3001 else if (contain_subplans((Node *) wfunc))
3002 use_ma_code = false; /* subplans might contain volatile functions */
3003 else
3004 use_ma_code = true; /* yes, let's use it */
3005 if (use_ma_code)
3006 {
3007 peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
3008 peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
3009 peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
3010 finalextra = aggform->aggmfinalextra;
3011 finalmodify = aggform->aggmfinalmodify;
3012 aggtranstype = aggform->aggmtranstype;
3014 }
3015 else
3016 {
3017 peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
3018 peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
3019 peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
3020 finalextra = aggform->aggfinalextra;
3021 finalmodify = aggform->aggfinalmodify;
3022 aggtranstype = aggform->aggtranstype;
3024 }
3025
3026 /*
3027 * ExecInitWindowAgg already checked permission to call aggregate function
3028 * ... but we still need to check the component functions
3029 */
3030
3031 /* Check that aggregate owner has permission to call component fns */
3032 {
3034 Oid aggOwner;
3035
3037 ObjectIdGetDatum(wfunc->winfnoid));
3039 elog(ERROR, "cache lookup failed for function %u",
3040 wfunc->winfnoid);
3043
3045 ACL_EXECUTE);
3046 if (aclresult != ACLCHECK_OK)
3048 get_func_name(transfn_oid));
3049 InvokeFunctionExecuteHook(transfn_oid);
3050
3051 if (OidIsValid(invtransfn_oid))
3052 {
3054 ACL_EXECUTE);
3055 if (aclresult != ACLCHECK_OK)
3057 get_func_name(invtransfn_oid));
3058 InvokeFunctionExecuteHook(invtransfn_oid);
3059 }
3060
3061 if (OidIsValid(finalfn_oid))
3062 {
3064 ACL_EXECUTE);
3065 if (aclresult != ACLCHECK_OK)
3067 get_func_name(finalfn_oid));
3068 InvokeFunctionExecuteHook(finalfn_oid);
3069 }
3070 }
3071
3072 /*
3073 * If the selected finalfn isn't read-only, we can't run this aggregate as
3074 * a window function. This is a user-facing error, so we take a bit more
3075 * care with the error message than elsewhere in this function.
3076 */
3078 ereport(ERROR,
3080 errmsg("aggregate function %s does not support use as a window function",
3081 format_procedure(wfunc->winfnoid))));
3082
3083 /* Detect how many arguments to pass to the finalfn */
3084 if (finalextra)
3085 peraggstate->numFinalArgs = numArguments + 1;
3086 else
3087 peraggstate->numFinalArgs = 1;
3088
3089 /* resolve actual type of transition state, if polymorphic */
3090 aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
3091 aggtranstype,
3092 inputTypes,
3093 numArguments);
3094
3095 /* build expression trees using actual argument & result types */
3097 numArguments,
3098 0, /* no ordered-set window functions yet */
3099 false, /* no variadic window functions yet */
3100 aggtranstype,
3101 wfunc->inputcollid,
3102 transfn_oid,
3103 invtransfn_oid,
3104 &transfnexpr,
3106
3107 /* set up infrastructure for calling the transfn(s) and finalfn */
3108 fmgr_info(transfn_oid, &peraggstate->transfn);
3110
3111 if (OidIsValid(invtransfn_oid))
3112 {
3113 fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
3115 }
3116
3117 if (OidIsValid(finalfn_oid))
3118 {
3120 peraggstate->numFinalArgs,
3121 aggtranstype,
3122 wfunc->wintype,
3123 wfunc->inputcollid,
3124 finalfn_oid,
3125 &finalfnexpr);
3126 fmgr_info(finalfn_oid, &peraggstate->finalfn);
3128 }
3129
3130 /* get info about relevant datatypes */
3131 get_typlenbyval(wfunc->wintype,
3132 &peraggstate->resulttypeLen,
3133 &peraggstate->resulttypeByVal);
3134 get_typlenbyval(aggtranstype,
3135 &peraggstate->transtypeLen,
3136 &peraggstate->transtypeByVal);
3137
3138 /*
3139 * initval is potentially null, so don't try to access it as a struct
3140 * field. Must do it the hard way with SysCacheGetAttr.
3141 */
3143 &peraggstate->initValueIsNull);
3144
3145 if (peraggstate->initValueIsNull)
3146 peraggstate->initValue = (Datum) 0;
3147 else
3149 aggtranstype);
3150
3151 /*
3152 * If the transfn is strict and the initval is NULL, make sure input type
3153 * and transtype are the same (or at least binary-compatible), so that
3154 * it's OK to use the first input value as the initial transValue. This
3155 * should have been checked at agg definition time, but we must check
3156 * again in case the transfn's strictness property has been changed.
3157 */
3158 if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
3159 {
3160 if (numArguments < 1 ||
3161 !IsBinaryCoercible(inputTypes[0], aggtranstype))
3162 ereport(ERROR,
3164 errmsg("aggregate %u needs to have compatible input type and transition type",
3165 wfunc->winfnoid)));
3166 }
3167
3168 /*
3169 * Insist that forward and inverse transition functions have the same
3170 * strictness setting. Allowing them to differ would require handling
3171 * more special cases in advance_windowaggregate and
3172 * advance_windowaggregate_base, for no discernible benefit. This should
3173 * have been checked at agg definition time, but we must check again in
3174 * case either function's strictness property has been changed.
3175 */
3176 if (OidIsValid(invtransfn_oid) &&
3177 peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
3178 ereport(ERROR,
3180 errmsg("strictness of aggregate's forward and inverse transition functions must match")));
3181
3182 /*
3183 * Moving aggregates use their own aggcontext.
3184 *
3185 * This is necessary because they might restart at different times, so we
3186 * might never be able to reset the shared context otherwise. We can't
3187 * make it the aggregates' responsibility to clean up after themselves,
3188 * because strict aggregates must be restarted whenever we remove their
3189 * last non-NULL input, which the aggregate won't be aware is happening.
3190 * Also, just pfree()ing the transValue upon restarting wouldn't help,
3191 * since we'd miss any indirectly referenced data. We could, in theory,
3192 * make the memory allocation rules for moving aggregates different than
3193 * they have historically been for plain aggregates, but that seems grotty
3194 * and likely to lead to memory leaks.
3195 */
3196 if (OidIsValid(invtransfn_oid))
3197 peraggstate->aggcontext =
3199 "WindowAgg Per Aggregate",
3201 else
3202 peraggstate->aggcontext = winstate->aggcontext;
3203
3205
3206 return peraggstate;
3207}
3208
3209static Datum
3211{
3212 Oid typinput,
3213 typioparam;
3214 char *strInitVal;
3215 Datum initVal;
3216
3217 getTypeInputInfo(transtype, &typinput, &typioparam);
3220 typioparam, -1);
3222 return initVal;
3223}
3224
3225/*
3226 * are_peers
3227 * compare two rows to see if they are equal according to the ORDER BY clause
3228 *
3229 * NB: this does not consider the window frame mode.
3230 */
3231static bool
3234{
3235 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3236 ExprContext *econtext = winstate->tmpcontext;
3237
3238 /* If no ORDER BY, all rows are peers with each other */
3239 if (node->ordNumCols == 0)
3240 return true;
3241
3242 econtext->ecxt_outertuple = slot1;
3243 econtext->ecxt_innertuple = slot2;
3244 return ExecQualAndReset(winstate->ordEqfunction, econtext);
3245}
3246
3247/*
3248 * window_gettupleslot
3249 * Fetch the pos'th tuple of the current partition into the slot,
3250 * using the winobj's read pointer
3251 *
3252 * Returns true if successful, false if no such row
3253 */
3254static bool
3256{
3257 WindowAggState *winstate = winobj->winstate;
3258 MemoryContext oldcontext;
3259
3260 /* often called repeatedly in a row */
3262
3263 /* Don't allow passing -1 to spool_tuples here */
3264 if (pos < 0)
3265 return false;
3266
3267 /* If necessary, fetch the tuple into the spool */
3268 spool_tuples(winstate, pos);
3269
3270 if (pos >= winstate->spooled_rows)
3271 return false;
3272
3273 if (pos < winobj->markpos)
3274 elog(ERROR, "cannot fetch row before WindowObject's mark position");
3275
3277
3278 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3279
3280 /*
3281 * Advance or rewind until we are within one tuple of the one we want.
3282 */
3283 if (winobj->seekpos < pos - 1)
3284 {
3285 if (!tuplestore_skiptuples(winstate->buffer,
3286 pos - 1 - winobj->seekpos,
3287 true))
3288 elog(ERROR, "unexpected end of tuplestore");
3289 winobj->seekpos = pos - 1;
3290 }
3291 else if (winobj->seekpos > pos + 1)
3292 {
3293 if (!tuplestore_skiptuples(winstate->buffer,
3294 winobj->seekpos - (pos + 1),
3295 false))
3296 elog(ERROR, "unexpected end of tuplestore");
3297 winobj->seekpos = pos + 1;
3298 }
3299 else if (winobj->seekpos == pos)
3300 {
3301 /*
3302 * There's no API to refetch the tuple at the current position. We
3303 * have to move one tuple forward, and then one backward. (We don't
3304 * do it the other way because we might try to fetch the row before
3305 * our mark, which isn't allowed.) XXX this case could stand to be
3306 * optimized.
3307 */
3308 tuplestore_advance(winstate->buffer, true);
3309 winobj->seekpos++;
3310 }
3311
3312 /*
3313 * Now we should be on the tuple immediately before or after the one we
3314 * want, so just fetch forwards or backwards as appropriate.
3315 *
3316 * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3317 * the fetched tuple. This ensures that the slot's contents remain valid
3318 * through manipulations of the tuplestore, which some callers depend on.
3319 */
3320 if (winobj->seekpos > pos)
3321 {
3322 if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3323 elog(ERROR, "unexpected end of tuplestore");
3324 winobj->seekpos--;
3325 }
3326 else
3327 {
3328 if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3329 elog(ERROR, "unexpected end of tuplestore");
3330 winobj->seekpos++;
3331 }
3332
3333 Assert(winobj->seekpos == pos);
3334
3335 MemoryContextSwitchTo(oldcontext);
3336
3337 return true;
3338}
3339
3340/*
3341 * gettuple_eval_partition
3342 * get tuple in a partition and evaluate the window function's argument
3343 * expression on it.
3344 */
3345static Datum
3347 int64 abs_pos, bool *isnull, bool *isout)
3348{
3349 WindowAggState *winstate;
3350 ExprContext *econtext;
3351 TupleTableSlot *slot;
3352
3353 winstate = winobj->winstate;
3354 slot = winstate->temp_slot_1;
3355 if (!window_gettupleslot(winobj, abs_pos, slot))
3356 {
3357 /* out of partition */
3358 if (isout)
3359 *isout = true;
3360 *isnull = true;
3361 return (Datum) 0;
3362 }
3363
3364 if (isout)
3365 *isout = false;
3366 econtext = winstate->ss.ps.ps_ExprContext;
3367 econtext->ecxt_outertuple = slot;
3369 (winobj->argstates, argno),
3370 econtext, isnull);
3371}
3372
3373/*
3374 * ignorenulls_getfuncarginframe
3375 * For IGNORE NULLS, get the next nonnull value in the frame, moving forward
3376 * or backward until we find a value or reach the frame's end.
3377 */
3378static Datum
3380 int relpos, int seektype, bool set_mark,
3381 bool *isnull, bool *isout)
3382{
3383 WindowAggState *winstate;
3384 ExprContext *econtext;
3385 TupleTableSlot *slot;
3386 Datum datum;
3387 int64 abs_pos;
3389 int notnull_offset;
3390 int notnull_relpos;
3391 int forward;
3392
3393 Assert(WindowObjectIsValid(winobj));
3394 winstate = winobj->winstate;
3395 econtext = winstate->ss.ps.ps_ExprContext;
3396 slot = winstate->temp_slot_1;
3397 datum = (Datum) 0;
3398 notnull_offset = 0;
3400
3401 switch (seektype)
3402 {
3404 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3405 abs_pos = mark_pos = 0; /* keep compiler quiet */
3406 break;
3407 case WINDOW_SEEK_HEAD:
3408 /* rejecting relpos < 0 is easy and simplifies code below */
3409 if (relpos < 0)
3410 goto out_of_frame;
3411 update_frameheadpos(winstate);
3412 abs_pos = winstate->frameheadpos;
3413 mark_pos = winstate->frameheadpos;
3414 forward = 1;
3415 break;
3416 case WINDOW_SEEK_TAIL:
3417 /* rejecting relpos > 0 is easy and simplifies code below */
3418 if (relpos > 0)
3419 goto out_of_frame;
3420 update_frametailpos(winstate);
3421 abs_pos = winstate->frametailpos - 1;
3422 mark_pos = 0; /* keep compiler quiet */
3423 forward = -1;
3424 break;
3425 default:
3426 elog(ERROR, "unrecognized window seek type: %d", seektype);
3427 abs_pos = mark_pos = 0; /* keep compiler quiet */
3428 break;
3429 }
3430
3431 /*
3432 * Get the next nonnull value in the frame, moving forward or backward
3433 * until we find a value or reach the frame's end.
3434 */
3435 do
3436 {
3437 int inframe;
3438 int v;
3439
3440 /*
3441 * Check apparent out of frame case. We need to do this because we
3442 * may not call window_gettupleslot before row_is_in_frame, which
3443 * supposes abs_pos is never negative.
3444 */
3445 if (abs_pos < 0)
3446 goto out_of_frame;
3447
3448 /* check whether row is in frame */
3449 inframe = row_is_in_frame(winobj, abs_pos, slot, true);
3450 if (inframe == -1)
3451 goto out_of_frame;
3452 else if (inframe == 0)
3453 goto advance;
3454
3455 if (isout)
3456 *isout = false;
3457
3458 v = get_notnull_info(winobj, abs_pos, argno);
3459 if (v == NN_NULL) /* this row is known to be NULL */
3460 goto advance;
3461
3462 else if (v == NN_UNKNOWN) /* need to check NULL or not */
3463 {
3464 if (!window_gettupleslot(winobj, abs_pos, slot))
3465 goto out_of_frame;
3466
3467 econtext->ecxt_outertuple = slot;
3468 datum = ExecEvalExpr(
3469 (ExprState *) list_nth(winobj->argstates,
3470 argno), econtext,
3471 isnull);
3472 if (!*isnull)
3474
3475 /* record the row status */
3476 put_notnull_info(winobj, abs_pos, argno, *isnull);
3477 }
3478 else /* this row is known to be NOT NULL */
3479 {
3482 {
3483 /* to prepare exiting this loop, datum needs to be set */
3484 if (!window_gettupleslot(winobj, abs_pos, slot))
3485 goto out_of_frame;
3486
3487 econtext->ecxt_outertuple = slot;
3488 datum = ExecEvalExpr(
3490 (winobj->argstates, argno),
3491 econtext, isnull);
3492 }
3493 }
3494advance:
3495 abs_pos += forward;
3496 } while (notnull_offset <= notnull_relpos);
3497
3498 if (set_mark)
3500
3501 return datum;
3502
3504 if (isout)
3505 *isout = true;
3506 *isnull = true;
3507 return (Datum) 0;
3508}
3509
3510
3511/*
3512 * init_notnull_info
3513 * Initialize non null map.
3514 */
3515static void
3517{
3518 int numargs = perfuncstate->numArguments;
3519
3520 if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3521 {
3522 int argno = 0;
3523 ListCell *lc;
3524
3525 winobj->notnull_info = palloc0_array(uint8 *, numargs);
3526 winobj->num_notnull_info = palloc0_array(int64, numargs);
3527 winobj->notnull_info_cacheable = palloc_array(bool, numargs);
3528
3529 foreach(lc, perfuncstate->wfunc->args)
3530 {
3531 Node *arg = (Node *) lfirst(lc);
3532
3533 winobj->notnull_info_cacheable[argno] =
3536
3537 argno++;
3538 }
3539 }
3540}
3541
3542/*
3543 * grow_notnull_info
3544 * expand notnull_info if necessary.
3545 * pos: not null info position
3546 * argno: argument number
3547 */
3548static void
3550{
3551/* initial number of notnull info members */
3552#define INIT_NOT_NULL_INFO_NUM 128
3553
3554 if (pos >= winobj->num_notnull_info[argno])
3555 {
3556 /* We may be called in a short-lived context */
3559
3560 for (;;)
3561 {
3563 (winobj->num_notnull_info[argno]);
3564 Size newsize;
3565
3566 if (oldsize == 0) /* memory has not been allocated yet for this
3567 * arg */
3568 {
3570 winobj->notnull_info[argno] = palloc0(newsize);
3571 }
3572 else
3573 {
3574 newsize = oldsize * 2;
3575 winobj->notnull_info[argno] =
3577 }
3579 if (winobj->num_notnull_info[argno] > pos)
3580 break;
3581 }
3582 MemoryContextSwitchTo(oldcontext);
3583 }
3584}
3585
3586/*
3587 * get_notnull_info
3588 * retrieve a map
3589 * pos: map position
3590 * argno: argument number
3591 */
3592static uint8
3594{
3595 uint8 *mbp;
3596 uint8 mb;
3597 int64 bpos;
3598
3599 if (!winobj->notnull_info_cacheable[argno])
3600 return NN_UNKNOWN;
3601
3602 grow_notnull_info(winobj, pos, argno);
3603 bpos = NN_POS_TO_BYTES(pos);
3604 mbp = winobj->notnull_info[argno];
3605 mb = mbp[bpos];
3606 return (mb >> (NN_SHIFT(pos))) & NN_MASK;
3607}
3608
3609/*
3610 * put_notnull_info
3611 * update map
3612 * pos: map position
3613 * argno: argument number
3614 * isnull: indicate NULL or NOT
3615 */
3616static void
3617put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull)
3618{
3619 uint8 *mbp;
3620 uint8 mb;
3621 int64 bpos;
3622 uint8 val = isnull ? NN_NULL : NN_NOTNULL;
3623 int shift;
3624
3625 if (!winobj->notnull_info_cacheable[argno])
3626 return;
3627
3628 grow_notnull_info(winobj, pos, argno);
3629 bpos = NN_POS_TO_BYTES(pos);
3630 mbp = winobj->notnull_info[argno];
3631 mb = mbp[bpos];
3632 shift = NN_SHIFT(pos);
3633 mb &= ~(NN_MASK << shift); /* clear map */
3634 mb |= (val << shift); /* update map */
3635 mbp[bpos] = mb;
3636}
3637
3638/***********************************************************************
3639 * API exposed to window functions
3640 ***********************************************************************/
3641
3642
3643/*
3644 * WinCheckAndInitializeNullTreatment
3645 * Check null treatment clause and sets ignore_nulls
3646 *
3647 * Window functions should call this to check if they are being called with
3648 * a null treatment clause when they don't allow it, or to set ignore_nulls.
3649 */
3650void
3652 bool allowNullTreatment,
3653 FunctionCallInfo fcinfo)
3654{
3655 Assert(WindowObjectIsValid(winobj));
3657 {
3658 const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
3659
3660 if (!funcname)
3661 elog(ERROR, "could not get function name");
3662 ereport(ERROR,
3664 errmsg("function %s does not allow RESPECT/IGNORE NULLS",
3665 funcname)));
3666 }
3667 else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3668 winobj->ignore_nulls = IGNORE_NULLS;
3669}
3670
3671/*
3672 * WinGetPartitionLocalMemory
3673 * Get working memory that lives till end of partition processing
3674 *
3675 * On first call within a given partition, this allocates and zeroes the
3676 * requested amount of space. Subsequent calls just return the same chunk.
3677 *
3678 * Memory obtained this way is normally used to hold state that should be
3679 * automatically reset for each new partition. If a window function wants
3680 * to hold state across the whole query, fcinfo->fn_extra can be used in the
3681 * usual way for that.
3682 */
3683void *
3685{
3686 Assert(WindowObjectIsValid(winobj));
3687 if (winobj->localmem == NULL)
3688 winobj->localmem =
3690 return winobj->localmem;
3691}
3692
3693/*
3694 * WinGetCurrentPosition
3695 * Return the current row's position (counting from 0) within the current
3696 * partition.
3697 */
3698int64
3700{
3701 Assert(WindowObjectIsValid(winobj));
3702 return winobj->winstate->currentpos;
3703}
3704
3705/*
3706 * WinGetPartitionRowCount
3707 * Return total number of rows contained in the current partition.
3708 *
3709 * Note: this is a relatively expensive operation because it forces the
3710 * whole partition to be "spooled" into the tuplestore at once. Once
3711 * executed, however, additional calls within the same partition are cheap.
3712 */
3713int64
3715{
3716 Assert(WindowObjectIsValid(winobj));
3717 spool_tuples(winobj->winstate, -1);
3718 return winobj->winstate->spooled_rows;
3719}
3720
3721/*
3722 * WinSetMarkPosition
3723 * Set the "mark" position for the window object, which is the oldest row
3724 * number (counting from 0) it is allowed to fetch during all subsequent
3725 * operations within the current partition.
3726 *
3727 * Window functions do not have to call this, but are encouraged to move the
3728 * mark forward when possible to keep the tuplestore size down and prevent
3729 * having to spill rows to disk.
3730 */
3731void
3733{
3734 WindowAggState *winstate;
3735
3736 Assert(WindowObjectIsValid(winobj));
3737 winstate = winobj->winstate;
3738
3739 if (markpos < winobj->markpos)
3740 elog(ERROR, "cannot move WindowObject's mark position backward");
3741 tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3742 if (markpos > winobj->markpos)
3743 {
3744 tuplestore_skiptuples(winstate->buffer,
3745 markpos - winobj->markpos,
3746 true);
3747 winobj->markpos = markpos;
3748 }
3749 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3750 if (markpos > winobj->seekpos)
3751 {
3752 tuplestore_skiptuples(winstate->buffer,
3753 markpos - winobj->seekpos,
3754 true);
3755 winobj->seekpos = markpos;
3756 }
3757}
3758
3759/*
3760 * WinRowsArePeers
3761 * Compare two rows (specified by absolute position in partition) to see
3762 * if they are equal according to the ORDER BY clause.
3763 *
3764 * NB: this does not consider the window frame mode.
3765 */
3766bool
3768{
3769 WindowAggState *winstate;
3770 WindowAgg *node;
3773 bool res;
3774
3775 Assert(WindowObjectIsValid(winobj));
3776 winstate = winobj->winstate;
3777 node = (WindowAgg *) winstate->ss.ps.plan;
3778
3779 /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3780 if (node->ordNumCols == 0)
3781 return true;
3782
3783 /*
3784 * Note: OK to use temp_slot_2 here because we aren't calling any
3785 * frame-related functions (those tend to clobber temp_slot_2).
3786 */
3787 slot1 = winstate->temp_slot_1;
3788 slot2 = winstate->temp_slot_2;
3789
3790 if (!window_gettupleslot(winobj, pos1, slot1))
3791 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3792 pos1);
3793 if (!window_gettupleslot(winobj, pos2, slot2))
3794 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3795 pos2);
3796
3797 res = are_peers(winstate, slot1, slot2);
3798
3801
3802 return res;
3803}
3804
3805/*
3806 * WinGetFuncArgInPartition
3807 * Evaluate a window function's argument expression on a specified
3808 * row of the partition. The row is identified in lseek(2) style,
3809 * i.e. relative to the current, first, or last row.
3810 *
3811 * argno: argument number to evaluate (counted from 0)
3812 * relpos: signed rowcount offset from the seek position
3813 * seektype: WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, or WINDOW_SEEK_TAIL
3814 * set_mark: If the row is found and set_mark is true, the mark is moved to
3815 * the row as a side-effect.
3816 * isnull: output argument, receives isnull status of result
3817 * isout: output argument, set to indicate whether target row position
3818 * is out of partition (can pass NULL if caller doesn't care about this)
3819 *
3820 * Specifying a nonexistent row is not an error, it just causes a null result
3821 * (plus setting *isout true, if isout isn't NULL).
3822 */
3823Datum
3825 int relpos, int seektype, bool set_mark,
3826 bool *isnull, bool *isout)
3827{
3828 WindowAggState *winstate;
3829 int64 abs_pos;
3831 Datum datum;
3832 bool null_treatment;
3833 int notnull_offset;
3834 int notnull_relpos;
3835 int forward;
3836 bool myisout;
3837 bool got_datum;
3838
3839 Assert(WindowObjectIsValid(winobj));
3840 winstate = winobj->winstate;
3841
3842 null_treatment = (winobj->ignore_nulls == IGNORE_NULLS && relpos != 0);
3843
3844 switch (seektype)
3845 {
3847 if (null_treatment)
3848 abs_pos = winstate->currentpos;
3849 else
3850 abs_pos = winstate->currentpos + relpos;
3851 break;
3852 case WINDOW_SEEK_HEAD:
3853 if (null_treatment)
3854 abs_pos = 0;
3855 else
3856 abs_pos = relpos;
3857 break;
3858 case WINDOW_SEEK_TAIL:
3859 spool_tuples(winstate, -1);
3860 abs_pos = winstate->spooled_rows - 1 + relpos;
3861 break;
3862 default:
3863 elog(ERROR, "unrecognized window seek type: %d", seektype);
3864 abs_pos = 0; /* keep compiler quiet */
3865 break;
3866 }
3867
3868 /* Easy case if IGNORE NULLS is not specified */
3869 if (!null_treatment)
3870 {
3871 /* get tuple and evaluate in partition */
3872 datum = gettuple_eval_partition(winobj, argno,
3873 abs_pos, isnull, &myisout);
3874 if (!myisout && set_mark)
3875 WinSetMarkPosition(winobj, abs_pos);
3876 if (isout)
3877 *isout = myisout;
3878 return datum;
3879 }
3880
3881 /* Prepare for loop */
3882 notnull_offset = 0;
3884 forward = relpos > 0 ? 1 : -1;
3885 myisout = false;
3886 got_datum = false;
3887 datum = 0;
3888
3889 /*
3890 * IGNORE NULLS + WINDOW_SEEK_CURRENT + relpos > 0 case, we would fetch
3891 * beyond the current row + relpos to find out the target row. If we mark
3892 * at abs_pos, next call to WinGetFuncArgInPartition or
3893 * WinGetFuncArgInFrame (in case when a window function have multiple
3894 * args) could fail with "cannot fetch row before WindowObject's mark
3895 * position". So keep the mark position at currentpos.
3896 */
3897 if (seektype == WINDOW_SEEK_CURRENT && relpos > 0)
3898 mark_pos = winstate->currentpos;
3899 else
3900 {
3901 /*
3902 * For other cases we have no idea what position of row callers would
3903 * fetch next time. Also for relpos < 0 case (we go backward), we
3904 * cannot set mark either. For those cases we always set mark at 0.
3905 */
3906 mark_pos = 0;
3907 }
3908
3909 /*
3910 * Get the next nonnull value in the partition, moving forward or backward
3911 * until we find a value or reach the partition's end. We cache the
3912 * nullness status because we may repeat this process many times.
3913 */
3914 do
3915 {
3916 int nn_info; /* NOT NULL status */
3917
3918 abs_pos += forward;
3919 if (abs_pos < 0) /* clearly out of partition */
3920 break;
3921
3922 /* check NOT NULL cached info */
3924 if (nn_info == NN_NOTNULL) /* this row is known to be NOT NULL */
3926 else if (nn_info == NN_NULL) /* this row is known to be NULL */
3927 continue; /* keep on moving forward or backward */
3928 else /* need to check NULL or not */
3929 {
3930 /*
3931 * NOT NULL info does not exist yet. Get tuple and evaluate func
3932 * arg in partition. Keep the return value in case this row is the
3933 * target; re-evaluating a volatile argument could give a
3934 * different nullness status.
3935 */
3936 datum = gettuple_eval_partition(winobj, argno,
3937 abs_pos, isnull, &myisout);
3938 if (myisout) /* out of partition? */
3939 break;
3940 if (!*isnull)
3941 {
3944 got_datum = true;
3945 }
3946 /* record the row status */
3947 put_notnull_info(winobj, abs_pos, argno, *isnull);
3948 }
3949 } while (notnull_offset < notnull_relpos);
3950
3951 /* get tuple and evaluate func arg in partition */
3952 if (!got_datum)
3953 datum = gettuple_eval_partition(winobj, argno,
3954 abs_pos, isnull, &myisout);
3955 if (!myisout && set_mark)
3957 if (isout)
3958 *isout = myisout;
3959
3960 return datum;
3961}
3962
3963/*
3964 * WinGetFuncArgInFrame
3965 * Evaluate a window function's argument expression on a specified
3966 * row of the window frame. The row is identified in lseek(2) style,
3967 * i.e. relative to the first or last row of the frame. (We do not
3968 * support WINDOW_SEEK_CURRENT here, because it's not very clear what
3969 * that should mean if the current row isn't part of the frame.)
3970 *
3971 * argno: argument number to evaluate (counted from 0)
3972 * relpos: signed rowcount offset from the seek position
3973 * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
3974 * set_mark: If the row is found/in frame and set_mark is true, the mark is
3975 * moved to the row as a side-effect.
3976 * isnull: output argument, receives isnull status of result
3977 * isout: output argument, set to indicate whether target row position
3978 * is out of frame (can pass NULL if caller doesn't care about this)
3979 *
3980 * Specifying a nonexistent or not-in-frame row is not an error, it just
3981 * causes a null result (plus setting *isout true, if isout isn't NULL).
3982 *
3983 * Note that some exclusion-clause options lead to situations where the
3984 * rows that are in-frame are not consecutive in the partition. But we
3985 * count only in-frame rows when measuring relpos.
3986 *
3987 * The set_mark flag is interpreted as meaning that the caller will specify
3988 * a constant (or, perhaps, monotonically increasing) relpos in successive
3989 * calls, so that *if there is no exclusion clause* there will be no need
3990 * to fetch a row before the previously fetched row. But we do not expect
3991 * the caller to know how to account for exclusion clauses. Therefore,
3992 * if there is an exclusion clause we take responsibility for adjusting the
3993 * mark request to something that will be safe given the above assumption
3994 * about relpos.
3995 */
3996Datum
3998 int relpos, int seektype, bool set_mark,
3999 bool *isnull, bool *isout)
4000{
4001 WindowAggState *winstate;
4002 ExprContext *econtext;
4003 TupleTableSlot *slot;
4004 int64 abs_pos;
4006
4007 Assert(WindowObjectIsValid(winobj));
4008 winstate = winobj->winstate;
4009 econtext = winstate->ss.ps.ps_ExprContext;
4010 slot = winstate->temp_slot_1;
4011
4012 if (winobj->ignore_nulls == IGNORE_NULLS)
4014 set_mark, isnull, isout);
4015
4016 switch (seektype)
4017 {
4019 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
4020 abs_pos = mark_pos = 0; /* keep compiler quiet */
4021 break;
4022 case WINDOW_SEEK_HEAD:
4023 /* rejecting relpos < 0 is easy and simplifies code below */
4024 if (relpos < 0)
4025 goto out_of_frame;
4026 update_frameheadpos(winstate);
4027 abs_pos = winstate->frameheadpos + relpos;
4028 mark_pos = abs_pos;
4029
4030 /*
4031 * Account for exclusion option if one is active, but advance only
4032 * abs_pos not mark_pos. This prevents changes of the current
4033 * row's peer group from resulting in trying to fetch a row before
4034 * some previous mark position.
4035 *
4036 * Note that in some corner cases such as current row being
4037 * outside frame, these calculations are theoretically too simple,
4038 * but it doesn't matter because we'll end up deciding the row is
4039 * out of frame. We do not attempt to avoid fetching rows past
4040 * end of frame; that would happen in some cases anyway.
4041 */
4042 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
4043 {
4044 case 0:
4045 /* no adjustment needed */
4046 break;
4048 if (abs_pos >= winstate->currentpos &&
4049 winstate->currentpos >= winstate->frameheadpos)
4050 abs_pos++;
4051 break;
4053 update_grouptailpos(winstate);
4054 if (abs_pos >= winstate->groupheadpos &&
4055 winstate->grouptailpos > winstate->frameheadpos)
4056 {
4057 int64 overlapstart = Max(winstate->groupheadpos,
4058 winstate->frameheadpos);
4059
4060 abs_pos += winstate->grouptailpos - overlapstart;
4061 }
4062 break;
4064 update_grouptailpos(winstate);
4065 if (abs_pos >= winstate->groupheadpos &&
4066 winstate->grouptailpos > winstate->frameheadpos)
4067 {
4068 int64 overlapstart = Max(winstate->groupheadpos,
4069 winstate->frameheadpos);
4070
4071 if (abs_pos == overlapstart)
4072 abs_pos = winstate->currentpos;
4073 else
4074 abs_pos += winstate->grouptailpos - overlapstart - 1;
4075 }
4076 break;
4077 default:
4078 elog(ERROR, "unrecognized frame option state: 0x%x",
4079 winstate->frameOptions);
4080 break;
4081 }
4082 break;
4083 case WINDOW_SEEK_TAIL:
4084 /* rejecting relpos > 0 is easy and simplifies code below */
4085 if (relpos > 0)
4086 goto out_of_frame;
4087 update_frametailpos(winstate);
4088 abs_pos = winstate->frametailpos - 1 + relpos;
4089
4090 /*
4091 * Account for exclusion option if one is active. If there is no
4092 * exclusion, we can safely set the mark at the accessed row. But
4093 * if there is, we can only mark the frame start, because we can't
4094 * be sure how far back in the frame the exclusion might cause us
4095 * to fetch in future. Furthermore, we have to actually check
4096 * against frameheadpos here, since it's unsafe to try to fetch a
4097 * row before frame start if the mark might be there already.
4098 */
4099 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
4100 {
4101 case 0:
4102 /* no adjustment needed */
4103 mark_pos = abs_pos;
4104 break;
4106 if (abs_pos <= winstate->currentpos &&
4107 winstate->currentpos < winstate->frametailpos)
4108 abs_pos--;
4109 update_frameheadpos(winstate);
4110 if (abs_pos < winstate->frameheadpos)
4111 goto out_of_frame;
4112 mark_pos = winstate->frameheadpos;
4113 break;
4115 update_grouptailpos(winstate);
4116 if (abs_pos < winstate->grouptailpos &&
4117 winstate->groupheadpos < winstate->frametailpos)
4118 {
4119 int64 overlapend = Min(winstate->grouptailpos,
4120 winstate->frametailpos);
4121
4122 abs_pos -= overlapend - winstate->groupheadpos;
4123 }
4124 update_frameheadpos(winstate);
4125 if (abs_pos < winstate->frameheadpos)
4126 goto out_of_frame;
4127 mark_pos = winstate->frameheadpos;
4128 break;
4130 update_grouptailpos(winstate);
4131 if (abs_pos < winstate->grouptailpos &&
4132 winstate->groupheadpos < winstate->frametailpos)
4133 {
4134 int64 overlapend = Min(winstate->grouptailpos,
4135 winstate->frametailpos);
4136
4137 if (abs_pos == overlapend - 1)
4138 abs_pos = winstate->currentpos;
4139 else
4140 abs_pos -= overlapend - 1 - winstate->groupheadpos;
4141 }
4142 update_frameheadpos(winstate);
4143 if (abs_pos < winstate->frameheadpos)
4144 goto out_of_frame;
4145 mark_pos = winstate->frameheadpos;
4146 break;
4147 default:
4148 elog(ERROR, "unrecognized frame option state: 0x%x",
4149 winstate->frameOptions);
4150 mark_pos = 0; /* keep compiler quiet */
4151 break;
4152 }
4153 break;
4154 default:
4155 elog(ERROR, "unrecognized window seek type: %d", seektype);
4156 abs_pos = mark_pos = 0; /* keep compiler quiet */
4157 break;
4158 }
4159
4160 if (!window_gettupleslot(winobj, abs_pos, slot))
4161 goto out_of_frame;
4162
4163 /* The code above does not detect all out-of-frame cases, so check */
4164 if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
4165 goto out_of_frame;
4166
4167 if (isout)
4168 *isout = false;
4169 if (set_mark)
4171 econtext->ecxt_outertuple = slot;
4172 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4173 econtext, isnull);
4174
4176 if (isout)
4177 *isout = true;
4178 *isnull = true;
4179 return (Datum) 0;
4180}
4181
4182/*
4183 * WinGetFuncArgCurrent
4184 * Evaluate a window function's argument expression on the current row.
4185 *
4186 * argno: argument number to evaluate (counted from 0)
4187 * isnull: output argument, receives isnull status of result
4188 *
4189 * Note: this isn't quite equivalent to WinGetFuncArgInPartition or
4190 * WinGetFuncArgInFrame targeting the current row, because it will succeed
4191 * even if the WindowObject's mark has been set beyond the current row.
4192 * This should generally be used for "ordinary" arguments of a window
4193 * function, such as the offset argument of lead() or lag().
4194 */
4195Datum
4196WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
4197{
4198 WindowAggState *winstate;
4199 ExprContext *econtext;
4200
4201 Assert(WindowObjectIsValid(winobj));
4202 winstate = winobj->winstate;
4203
4204 econtext = winstate->ss.ps.ps_ExprContext;
4205
4206 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
4207 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4208 econtext, isnull);
4209}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3880
int16 AttrNumber
Definition attnum.h:21
#define TextDatumGetCString(d)
Definition builtins.h:99
#define pg_noinline
Definition c.h:321
#define Min(x, y)
Definition c.h:1091
uint8_t uint8
Definition c.h:622
#define Max(x, y)
Definition c.h:1085
#define INT64_FORMAT
Definition c.h:634
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
int16_t int16
Definition c.h:619
#define PG_INT64_MAX
Definition c.h:676
#define unlikely(x)
Definition c.h:438
#define MemSet(start, val, len)
Definition c.h:1107
#define OidIsValid(objectId)
Definition c.h:858
size_t Size
Definition c.h:689
uint32 result
bool contain_subplans(Node *clause)
Definition clauses.c:343
bool contain_volatile_functions(Node *clause)
Definition clauses.c:551
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
void ExecReScan(PlanState *node)
Definition execAmi.c:78
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:250
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
void ExecEndNode(PlanState *node)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition execUtils.c:709
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition execUtils.c:588
#define InstrCountFiltered1(node, delta)
Definition execnodes.h:1307
#define outerPlanState(node)
Definition execnodes.h:1299
@ WINDOWAGG_PASSTHROUGH
Definition execnodes.h:2522
@ WINDOWAGG_RUN
Definition execnodes.h:2521
@ WINDOWAGG_DONE
Definition execnodes.h:2520
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition execnodes.h:2523
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition executor.h:493
#define ResetExprContext(econtext)
Definition executor.h:661
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:529
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition executor.h:556
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:322
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:403
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:446
#define EXEC_FLAG_MARK
Definition executor.h:71
ExpandedObjectHeader * DatumGetEOHP(Datum d)
void DeleteExpandedObject(Datum d)
#define MakeExpandedObjectReadOnly(d, isnull, typlen)
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_array(type, count)
Definition fe_memutils.h:92
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition fmgr.c:1755
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition fmgr.c:1225
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition fmgr.c:139
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition fmgr.h:172
#define fmgr_info_set_expr(expr, finfo)
Definition fmgr.h:135
int work_mem
Definition globals.c:133
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
#define funcname
long val
Definition informix.c:689
static struct @177 value
static bool pg_add_s64_overflow(int64 a, int64 b, int64 *result)
Definition int.h:235
int j
Definition isn.c:78
int i
Definition isn.c:77
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2482
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition lsyscache.c:3107
char * get_func_name(Oid funcid)
Definition lsyscache.c:1839
void * repalloc0(void *pointer, Size oldsize, Size size)
Definition mcxt.c:1707
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition mcxt.c:783
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
Oid GetUserId(void)
Definition miscinit.c:470
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
Datum WinGetFuncArgInPartition(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
void * WinGetPartitionLocalMemory(WindowObject winobj, Size sz)
static void grow_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_MASK
#define INIT_NOT_NULL_INFO_NUM
static void begin_partition(WindowAggState *winstate)
static void update_grouptailpos(WindowAggState *winstate)
Datum WinGetFuncArgInFrame(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
#define NN_SHIFT(pos)
#define NN_UNKNOWN
static void init_notnull_info(WindowObject winobj, WindowStatePerFunc perfuncstate)
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
static void spool_tuples(WindowAggState *winstate, int64 pos)
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
void ExecEndWindowAgg(WindowAggState *node)
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
#define NN_NOTNULL
static void put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull)
static Datum ignorenulls_getfuncarginframe(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
static void finalize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
static bool advance_windowaggregate_base(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
static pg_noinline void prepare_tuplestore(WindowAggState *winstate)
void ExecReScanWindowAgg(WindowAggState *node)
int64 WinGetCurrentPosition(WindowObject winobj)
WindowAggState * ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
bool WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
static pg_noinline void calculate_frame_offsets(PlanState *pstate)
#define NN_BYTES_TO_POS(bytes)
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
#define NN_NULL
static void eval_windowaggregates(WindowAggState *winstate)
static uint8 get_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_POS_TO_BYTES(pos)
static int row_is_in_frame(WindowObject winobj, int64 pos, TupleTableSlot *slot, bool fetch_tuple)
static void release_partition(WindowAggState *winstate)
static Datum gettuple_eval_partition(WindowObject winobj, int argno, int64 abs_pos, bool *isnull, bool *isout)
static void update_frametailpos(WindowAggState *winstate)
static void update_frameheadpos(WindowAggState *winstate)
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
void WinCheckAndInitializeNullTreatment(WindowObject winobj, bool allowNullTreatment, FunctionCallInfo fcinfo)
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
Datum WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
int64 WinGetPartitionRowCount(WindowObject winobj)
NodeTag
Definition nodes.h:27
#define makeNode(_type_)
Definition nodes.h:161
#define castNode(_type_, nodeptr)
Definition nodes.h:182
static char * errmsg
#define InvokeFunctionExecuteHook(objectId)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
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:2359
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition parse_agg.c:2148
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:2251
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_END_CURRENT_ROW
Definition parsenodes.h:622
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:633
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition parsenodes.h:627
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition parsenodes.h:624
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition parsenodes.h:617
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:621
#define FRAMEOPTION_START_OFFSET
Definition parsenodes.h:631
@ OBJECT_FUNCTION
#define FRAMEOPTION_EXCLUDE_TIES
Definition parsenodes.h:629
#define FRAMEOPTION_RANGE
Definition parsenodes.h:613
#define FRAMEOPTION_EXCLUDE_GROUP
Definition parsenodes.h:628
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:615
#define ACL_EXECUTE
Definition parsenodes.h:83
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition parsenodes.h:620
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition parsenodes.h:623
#define FRAMEOPTION_EXCLUSION
Definition parsenodes.h:635
#define FRAMEOPTION_ROWS
Definition parsenodes.h:614
END_CATALOG_STRUCT typedef FormData_pg_aggregate * Form_pg_aggregate
#define FUNC_MAX_ARGS
const void size_t len
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
END_CATALOG_STRUCT typedef FormData_pg_proc * Form_pg_proc
Definition pg_proc.h:140
#define outerPlan(node)
Definition plannodes.h:267
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static int64 DatumGetInt64(Datum X)
Definition postgres.h:416
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define InvalidOid
unsigned int Oid
static int fb(int x)
#define PARSER_IGNORE_NULLS
Definition primnodes.h:590
#define IGNORE_NULLS
Definition primnodes.h:592
#define NO_NULLTREATMENT
Definition primnodes.h:589
char * format_procedure(Oid procedure_oid)
Definition regproc.c:305
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:289
Datum * ecxt_aggvalues
Definition execnodes.h:306
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
bool * ecxt_aggnulls
Definition execnodes.h:308
MemoryContext ecxt_per_query_memory
Definition execnodes.h:294
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:291
Expr * expr
Definition execnodes.h:132
Oid fn_oid
Definition fmgr.h:59
FmgrInfo * flinfo
Definition fmgr.h:87
Definition pg_list.h:54
Definition nodes.h:135
bool outeropsset
Definition execnodes.h:1286
const TupleTableSlotOps * outerops
Definition execnodes.h:1278
ExprState * qual
Definition execnodes.h:1224
Plan * plan
Definition execnodes.h:1201
bool outeropsfixed
Definition execnodes.h:1282
EState * state
Definition execnodes.h:1203
ExprContext * ps_ExprContext
Definition execnodes.h:1242
ProjectionInfo * ps_ProjInfo
Definition execnodes.h:1243
ExecProcNodeMtd ExecProcNode
Definition execnodes.h:1207
List * qual
Definition plannodes.h:237
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1662
PlanState ps
Definition execnodes.h:1659
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
ExprState * endOffset
Definition execnodes.h:2557
MemoryContext aggcontext
Definition execnodes.h:2587
ScanState ss
Definition execnodes.h:2529
int64 aggregatedbase
Definition execnodes.h:2551
int64 frametailgroup
Definition execnodes.h:2582
int64 frameheadgroup
Definition execnodes.h:2581
WindowStatePerAgg peragg
Definition execnodes.h:2537
MemoryContext partcontext
Definition execnodes.h:2586
FmgrInfo endInRangeFunc
Definition execnodes.h:2563
TupleTableSlot * framehead_slot
Definition execnodes.h:2606
bool partition_spooled
Definition execnodes.h:2592
FmgrInfo startInRangeFunc
Definition execnodes.h:2562
Datum startOffsetValue
Definition execnodes.h:2558
TupleTableSlot * frametail_slot
Definition execnodes.h:2607
ExprState * ordEqfunction
Definition execnodes.h:2539
ExprState * runcondition
Definition execnodes.h:2574
TupleTableSlot * temp_slot_2
Definition execnodes.h:2612
Tuplestorestate * buffer
Definition execnodes.h:2540
WindowAggStatus status
Definition execnodes.h:2553
TupleTableSlot * agg_row_slot
Definition execnodes.h:2610
struct WindowObjectData * agg_winobj
Definition execnodes.h:2550
WindowStatePerFunc perfunc
Definition execnodes.h:2536
MemoryContext curaggcontext
Definition execnodes.h:2588
bool inRangeNullsFirst
Definition execnodes.h:2566
Datum endOffsetValue
Definition execnodes.h:2559
ExprState * partEqfunction
Definition execnodes.h:2538
ExprState * startOffset
Definition execnodes.h:2556
TupleTableSlot * first_part_slot
Definition execnodes.h:2604
int64 aggregatedupto
Definition execnodes.h:2552
ExprContext * tmpcontext
Definition execnodes.h:2589
TupleTableSlot * temp_slot_1
Definition execnodes.h:2611
bool use_pass_through
Definition execnodes.h:2569
int partNumCols
Definition plannodes.h:1262
Oid endInRangeFunc
Definition plannodes.h:1306
Node * endOffset
Definition plannodes.h:1292
bool topWindow
Definition plannodes.h:1321
Oid inRangeColl
Definition plannodes.h:1309
Node * startOffset
Definition plannodes.h:1289
List * runCondition
Definition plannodes.h:1295
Oid startInRangeFunc
Definition plannodes.h:1303
bool inRangeAsc
Definition plannodes.h:1312
Index winref
Definition plannodes.h:1259
bool inRangeNullsFirst
Definition plannodes.h:1315
int ordNumCols
Definition plannodes.h:1274
int frameOptions
Definition plannodes.h:1286
WindowFunc * wfunc
Definition execnodes.h:959
ExprState * aggfilter
Definition execnodes.h:961
List * args
Definition primnodes.h:606
Index winref
Definition primnodes.h:612
int ignore_nulls
Definition primnodes.h:618
int64 * num_notnull_info
WindowAggState * winstate
uint8 ** notnull_info
bool * notnull_info_cacheable
MemoryContext aggcontext
WindowFuncExprState * wfuncstate
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition tuplestore.c:743
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition tuplestore.c:508
void tuplestore_clear(Tuplestorestate *state)
Definition tuplestore.c:431
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition tuplestore.c:396
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331
void tuplestore_trim(Tuplestorestate *state)
bool tuplestore_advance(Tuplestorestate *state, bool forward)
bool tuplestore_in_memory(Tuplestorestate *state)
void tuplestore_end(Tuplestorestate *state)
Definition tuplestore.c:493
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition tuplestore.c:372
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544
#define WINDOW_SEEK_TAIL
Definition windowapi.h:36
#define WINDOW_SEEK_HEAD
Definition windowapi.h:35
#define WindowObjectIsValid(winobj)
Definition windowapi.h:43
#define WINDOW_SEEK_CURRENT
Definition windowapi.h:34