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