PostgreSQL Source Code git master
nodeWindowAgg.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
#include "windowapi.h"
Include dependency graph for nodeWindowAgg.c:

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

Macros

#define NN_UNKNOWN   0x00 /* value not calculated yet */
 
#define NN_NULL   0x01 /* NULL */
 
#define NN_NOTNULL   0x02 /* NOT NULL */
 
#define NN_MASK   0x03 /* mask for NOT NULL MAP */
 
#define NN_BITS_PER_MEMBER   2 /* number of bits in not null map */
 
#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)
 
#define NN_POS_TO_BYTES(pos)   ((pos) / NN_ITEM_PER_VAR)
 
#define NN_BYTES_TO_POS(bytes)   ((bytes) * NN_ITEM_PER_VAR)
 
#define NN_SHIFT(pos)   ((pos) % NN_ITEM_PER_VAR) * NN_BITS_PER_MEMBER
 
#define INIT_NOT_NULL_INFO_NUM   128
 

Typedefs

typedef struct WindowObjectData WindowObjectData
 
typedef struct WindowStatePerFuncData WindowStatePerFuncData
 
typedef struct WindowStatePerAggData WindowStatePerAggData
 

Functions

static void initialize_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static void advance_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static bool advance_windowaggregate_base (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
 
static void finalize_windowaggregate (WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
 
static void eval_windowaggregates (WindowAggState *winstate)
 
static void eval_windowfunction (WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
 
static void begin_partition (WindowAggState *winstate)
 
static void spool_tuples (WindowAggState *winstate, int64 pos)
 
static void release_partition (WindowAggState *winstate)
 
static int row_is_in_frame (WindowObject winobj, int64 pos, TupleTableSlot *slot, bool fetch_tuple)
 
static void update_frameheadpos (WindowAggState *winstate)
 
static void update_frametailpos (WindowAggState *winstate)
 
static void update_grouptailpos (WindowAggState *winstate)
 
static WindowStatePerAggDatainitialize_peragg (WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
 
static Datum GetAggInitVal (Datum textInitVal, Oid transtype)
 
static bool are_peers (WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
 
static bool window_gettupleslot (WindowObject winobj, int64 pos, TupleTableSlot *slot)
 
static Datum ignorenulls_getfuncarginframe (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
static Datum gettuple_eval_partition (WindowObject winobj, int argno, int64 abs_pos, bool *isnull, bool *isout)
 
static void init_notnull_info (WindowObject winobj, WindowStatePerFunc perfuncstate)
 
static void grow_notnull_info (WindowObject winobj, int64 pos, int argno)
 
static uint8 get_notnull_info (WindowObject winobj, int64 pos, int argno)
 
static void put_notnull_info (WindowObject winobj, int64 pos, int argno, bool isnull)
 
static pg_noinline void prepare_tuplestore (WindowAggState *winstate)
 
static pg_noinline void calculate_frame_offsets (PlanState *pstate)
 
static TupleTableSlotExecWindowAgg (PlanState *pstate)
 
WindowAggStateExecInitWindowAgg (WindowAgg *node, EState *estate, int eflags)
 
void ExecEndWindowAgg (WindowAggState *node)
 
void ExecReScanWindowAgg (WindowAggState *node)
 
void WinCheckAndInitializeNullTreatment (WindowObject winobj, bool allowNullTreatment, FunctionCallInfo fcinfo)
 
void * WinGetPartitionLocalMemory (WindowObject winobj, Size sz)
 
int64 WinGetCurrentPosition (WindowObject winobj)
 
int64 WinGetPartitionRowCount (WindowObject winobj)
 
void WinSetMarkPosition (WindowObject winobj, int64 markpos)
 
bool WinRowsArePeers (WindowObject winobj, int64 pos1, int64 pos2)
 
Datum WinGetFuncArgInPartition (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgInFrame (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgCurrent (WindowObject winobj, int argno, bool *isnull)
 

Macro Definition Documentation

◆ INIT_NOT_NULL_INFO_NUM

#define INIT_NOT_NULL_INFO_NUM   128

◆ NN_BITS_PER_MEMBER

#define NN_BITS_PER_MEMBER   2 /* number of bits in not null map */

Definition at line 235 of file nodeWindowAgg.c.

◆ NN_BYTES_TO_POS

#define NN_BYTES_TO_POS (   bytes)    ((bytes) * NN_ITEM_PER_VAR)

Definition at line 241 of file nodeWindowAgg.c.

◆ NN_ITEM_PER_VAR

#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)

Definition at line 237 of file nodeWindowAgg.c.

◆ NN_MASK

#define NN_MASK   0x03 /* mask for NOT NULL MAP */

Definition at line 234 of file nodeWindowAgg.c.

◆ NN_NOTNULL

#define NN_NOTNULL   0x02 /* NOT NULL */

Definition at line 233 of file nodeWindowAgg.c.

◆ NN_NULL

#define NN_NULL   0x01 /* NULL */

Definition at line 232 of file nodeWindowAgg.c.

◆ NN_POS_TO_BYTES

#define NN_POS_TO_BYTES (   pos)    ((pos) / NN_ITEM_PER_VAR)

Definition at line 239 of file nodeWindowAgg.c.

◆ NN_SHIFT

#define NN_SHIFT (   pos)    ((pos) % NN_ITEM_PER_VAR) * NN_BITS_PER_MEMBER

Definition at line 243 of file nodeWindowAgg.c.

◆ NN_UNKNOWN

#define NN_UNKNOWN   0x00 /* value not calculated yet */

Definition at line 231 of file nodeWindowAgg.c.

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

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

Definition at line 285 of file nodeWindowAgg.c.

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}
#define OidIsValid(objectId)
Definition: c.h:779
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 ereport(elevel,...)
Definition: elog.h:150
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:393
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
void DeleteExpandedObject(Datum d)
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
Assert(PointerIsAligned(start, uint64))
int i
Definition: isn.c:77
void pfree(void *pointer)
Definition: mcxt.c:1594
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:777
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
#define FUNC_MAX_ARGS
#define lfirst(lc)
Definition: pg_list.h:172
static bool DatumGetBool(Datum X)
Definition: postgres.h:100
uint64_t Datum
Definition: postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:281
bool fn_strict
Definition: fmgr.h:61
Definition: nodes.h:135
MemoryContext curaggcontext
Definition: execnodes.h:2689
ExprContext * tmpcontext
Definition: execnodes.h:2690
ExprState * aggfilter
Definition: execnodes.h:925
MemoryContext aggcontext
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:91

References WindowStatePerAggData::aggcontext, WindowFuncExprState::aggfilter, arg, WindowFuncExprState::args, Assert(), WindowAggState::curaggcontext, CurrentMemoryContext, datumCopy(), DatumGetBool(), DatumGetEOHP(), DatumGetPointer(), DatumIsReadWriteExpandedObject, DeleteExpandedObject(), ExprContext::ecxt_per_tuple_memory, ereport, errcode(), errmsg(), ERROR, ExecEvalExpr(), FmgrInfo::fn_strict, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, WindowStatePerAggData::invtransfn_oid, lfirst, LOCAL_FCINFO, MemoryContextGetParent(), MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, OidIsValid, pfree(), WindowAggState::tmpcontext, WindowStatePerAggData::transfn, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueCount, WindowStatePerAggData::transValueIsNull, WindowStatePerFuncData::wfuncstate, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

static bool advance_windowaggregate_base ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate 
)
static

Definition at line 462 of file nodeWindowAgg.c.

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}
#define elog(elevel,...)
Definition: elog.h:226
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
WindowStatePerFunc perfunc
Definition: execnodes.h:2637

References WindowStatePerAggData::aggcontext, WindowFuncExprState::aggfilter, arg, WindowFuncExprState::args, Assert(), WindowAggState::curaggcontext, CurrentMemoryContext, datumCopy(), DatumGetBool(), DatumGetEOHP(), DatumGetPointer(), DatumIsReadWriteExpandedObject, DeleteExpandedObject(), ExprContext::ecxt_per_tuple_memory, elog, ERROR, ExecEvalExpr(), FmgrInfo::fn_strict, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, initialize_windowaggregate(), WindowStatePerAggData::invtransfn, lfirst, LOCAL_FCINFO, MemoryContextGetParent(), MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, WindowAggState::perfunc, pfree(), WindowAggState::tmpcontext, WindowStatePerAggData::transtypeByVal, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueCount, WindowStatePerAggData::transValueIsNull, WindowStatePerAggData::wfuncno, WindowStatePerFuncData::wfuncstate, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

◆ are_peers()

static bool are_peers ( WindowAggState winstate,
TupleTableSlot slot1,
TupleTableSlot slot2 
)
static

Definition at line 3181 of file nodeWindowAgg.c.

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}
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:546
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Plan * plan
Definition: execnodes.h:1165
PlanState ps
Definition: execnodes.h:1621
ScanState ss
Definition: execnodes.h:2630
ExprState * ordEqfunction
Definition: execnodes.h:2640
int ordNumCols
Definition: plannodes.h:1253

References ExecQualAndReset(), if(), WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, and WindowAggState::tmpcontext.

Referenced by ExecWindowAgg(), row_is_in_frame(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), and WinRowsArePeers().

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1237 of file nodeWindowAgg.c.

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}
#define unlikely(x)
Definition: c.h:407
#define outerPlanState(node)
Definition: execnodes.h:1261
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:314
int j
Definition: isn.c:78
static pg_noinline void prepare_tuplestore(WindowAggState *winstate)
#define NN_POS_TO_BYTES(pos)
#define outerPlan(node)
Definition: plannodes.h:261
#define PARSER_IGNORE_NULLS
Definition: primnodes.h:589
#define IGNORE_NULLS
Definition: primnodes.h:591
int64 aggregatedbase
Definition: execnodes.h:2652
int64 frametailgroup
Definition: execnodes.h:2683
int64 frameheadgroup
Definition: execnodes.h:2682
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
int64 spooled_rows
Definition: execnodes.h:2646
int64 frameheadpos
Definition: execnodes.h:2648
bool more_partitions
Definition: execnodes.h:2696
int64 grouptailpos
Definition: execnodes.h:2685
int64 currentgroup
Definition: execnodes.h:2681
TupleTableSlot * frametail_slot
Definition: execnodes.h:2708
Tuplestorestate * buffer
Definition: execnodes.h:2641
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2711
struct WindowObjectData * agg_winobj
Definition: execnodes.h:2651
bool framehead_valid
Definition: execnodes.h:2698
int64 groupheadpos
Definition: execnodes.h:2684
bool grouptail_valid
Definition: execnodes.h:2702
int64 currentpos
Definition: execnodes.h:2647
int64 frametailpos
Definition: execnodes.h:2649
TupleTableSlot * first_part_slot
Definition: execnodes.h:2705
int64 aggregatedupto
Definition: execnodes.h:2653
int64 * num_notnull_info
Definition: nodeWindowAgg.c:73
uint8 ** notnull_info
Definition: nodeWindowAgg.c:72
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
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

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

Referenced by ExecWindowAgg().

◆ calculate_frame_offsets()

static pg_noinline void calculate_frame_offsets ( PlanState pstate)
static

Definition at line 2149 of file nodeWindowAgg.c.

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}
int64_t int64
Definition: c.h:540
int16_t int16
Definition: c.h:538
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:436
static struct @171 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2418
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:630
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:628
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:612
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:611
const void size_t len
static int64 DatumGetInt64(Datum X)
Definition: postgres.h:393
Expr * expr
Definition: execnodes.h:118
ExprContext * ps_ExprContext
Definition: execnodes.h:1204
ExprState * endOffset
Definition: execnodes.h:2658
Datum startOffsetValue
Definition: execnodes.h:2659
Datum endOffsetValue
Definition: execnodes.h:2660
ExprState * startOffset
Definition: execnodes.h:2657

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

Referenced by ExecWindowAgg().

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 706 of file nodeWindowAgg.c.

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}
#define ResetExprContext(econtext)
Definition: executor.h:650
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
static void advance_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static void finalize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate, Datum *result, bool *isnull)
static bool advance_windowaggregate_base(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
static bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
void WinSetMarkPosition(WindowObject winobj, int64 markpos)
static int row_is_in_frame(WindowObject winobj, int64 pos, TupleTableSlot *slot, bool fetch_tuple)
static void update_frameheadpos(WindowAggState *winstate)
#define FRAMEOPTION_END_CURRENT_ROW
Definition: parsenodes.h:619
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition: parsenodes.h:617
#define FRAMEOPTION_EXCLUSION
Definition: parsenodes.h:632
Datum * ecxt_aggvalues
Definition: execnodes.h:292
bool * ecxt_aggnulls
Definition: execnodes.h:294
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:277
MemoryContext aggcontext
Definition: execnodes.h:2688
WindowStatePerAgg peragg
Definition: execnodes.h:2638
TupleTableSlot * temp_slot_1
Definition: execnodes.h:2712

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

Referenced by ExecWindowAgg().

◆ eval_windowfunction()

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

Definition at line 1077 of file nodeWindowAgg.c.

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}

References WindowAggState::curaggcontext, datumCopy(), ExprContext::ecxt_per_tuple_memory, WindowStatePerFuncData::flinfo, FUNC_MAX_ARGS, FunctionCallInvoke, InitFunctionCallInfoData, LOCAL_FCINFO, MemoryContextSwitchTo(), WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, ScanState::ps, PlanState::ps_ExprContext, WindowStatePerFuncData::resulttypeByVal, WindowStatePerFuncData::resulttypeLen, WindowAggState::ss, WindowStatePerFuncData::winCollation, and WindowStatePerFuncData::winobj.

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2811 of file nodeWindowAgg.c.

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}
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition: execnodes.h:2687
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

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

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2498 of file nodeWindowAgg.c.

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));
2718 InvokeFunctionExecuteHook(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}
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
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:548
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
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
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
@ WINDOWAGG_RUN
Definition: execnodes.h:2622
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:2613
#define EXEC_FLAG_BACKWARD
Definition: executor.h:69
#define EXEC_FLAG_MARK
Definition: executor.h:70
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:128
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:138
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:135
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1775
void * palloc0(Size size)
Definition: mcxt.c:1395
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
Oid GetUserId(void)
Definition: miscinit.c:469
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
static void init_notnull_info(WindowObject winobj, WindowStatePerFunc perfuncstate)
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
#define makeNode(_type_)
Definition: nodes.h:161
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:213
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:618
@ OBJECT_FUNCTION
Definition: parsenodes.h:2344
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:610
#define ACL_EXECUTE
Definition: parsenodes.h:83
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:280
bool outeropsset
Definition: execnodes.h:1248
const TupleTableSlotOps * outerops
Definition: execnodes.h:1240
ExprState * qual
Definition: execnodes.h:1186
bool outeropsfixed
Definition: execnodes.h:1244
EState * state
Definition: execnodes.h:1167
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1171
List * qual
Definition: plannodes.h:231
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1624
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:122
FmgrInfo endInRangeFunc
Definition: execnodes.h:2664
FmgrInfo startInRangeFunc
Definition: execnodes.h:2663
ExprState * runcondition
Definition: execnodes.h:2675
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2713
WindowAggStatus status
Definition: execnodes.h:2654
bool inRangeNullsFirst
Definition: execnodes.h:2667
ExprState * partEqfunction
Definition: execnodes.h:2639
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 frameOptions
Definition: plannodes.h:1265
WindowFunc * wfunc
Definition: execnodes.h:923
Index winref
Definition: primnodes.h:611
WindowAggState * winstate
Definition: nodeWindowAgg.c:65

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, WindowStatePerFuncData::aggno, WindowAggState::all_first, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, WindowFuncExprState::args, WindowObjectData::argstates, Assert(), contain_volatile_functions(), CurrentMemoryContext, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_per_query_memory, elog, WindowAggState::endInRangeFunc, WindowAgg::endInRangeFunc, WindowAggState::endOffset, WindowAgg::endOffset, equal(), ERROR, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecAssignProjectionInfo(), ExecCreateScanSlotFromOuterPlan(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitQual(), ExecInitResultTupleSlotTL(), PlanState::ExecProcNode, execTuplesMatchPrepare(), ExecWindowAgg(), WindowAggState::first_part_slot, WindowStatePerFuncData::flinfo, fmgr_info(), fmgr_info_cxt(), fmgr_info_set_expr, WindowAggState::framehead_slot, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, WindowAggState::frameOptions, WindowAgg::frameOptions, WindowAggState::frametail_slot, WindowAggState::funcs, get_func_name(), get_typlenbyval(), GetUserId(), i, WindowObjectData::ignore_nulls, WindowFunc::ignore_nulls, init_notnull_info(), initialize_peragg(), WindowAggState::inRangeAsc, WindowAgg::inRangeAsc, WindowAggState::inRangeColl, WindowAgg::inRangeColl, WindowAggState::inRangeNullsFirst, WindowAgg::inRangeNullsFirst, InvokeFunctionExecuteHook, lfirst, list_length(), WindowObjectData::localmem, makeNode, WindowObjectData::markptr, WindowAggState::more_partitions, WindowAggState::next_partition, NIL, WindowAggState::numaggs, WindowStatePerFuncData::numArguments, WindowAggState::numfuncs, object_aclcheck(), OBJECT_FUNCTION, OidIsValid, WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::outerops, PlanState::outeropsfixed, PlanState::outeropsset, outerPlan, outerPlanState, palloc0(), WindowAggState::partcontext, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, WindowAggState::peragg, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, PlanState::plan, WindowAgg::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::qual, Plan::qual, WindowObjectData::readptr, WindowStatePerFuncData::resulttypeByVal, WindowStatePerFuncData::resulttypeLen, WindowAggState::runcondition, WindowAgg::runCondition, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::startInRangeFunc, WindowAgg::startInRangeFunc, WindowAggState::startOffset, WindowAgg::startOffset, PlanState::state, WindowAggState::status, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, WindowAggState::tmpcontext, WindowAggState::top_window, WindowAgg::topWindow, TupleTableSlot::tts_tupleDescriptor, TTSOpsMinimalTuple, TTSOpsVirtual, WindowAggState::use_pass_through, WindowStatePerFuncData::wfunc, WindowFuncExprState::wfunc, WindowStatePerAggData::wfuncno, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WindowStatePerFuncData::winCollation, WINDOWAGG_RUN, WindowFunc::winfnoid, WindowStatePerFuncData::winobj, WindowAgg::winref, WindowFunc::winref, and WindowObjectData::winstate.

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2846 of file nodeWindowAgg.c.

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}
#define MemSet(start, val, len)
Definition: c.h:1024
void ExecReScan(PlanState *node)
Definition: execAmi.c:77

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

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot * ExecWindowAgg ( PlanState pstate)
static

Definition at line 2230 of file nodeWindowAgg.c.

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}
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1269
@ WINDOWAGG_PASSTHROUGH
Definition: execnodes.h:2623
@ WINDOWAGG_DONE
Definition: execnodes.h:2621
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition: execnodes.h:2624
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:483
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:519
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
static void begin_partition(WindowAggState *winstate)
static void update_grouptailpos(WindowAggState *winstate)
static void spool_tuples(WindowAggState *winstate, int64 pos)
static void eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate, Datum *result, bool *isnull)
static pg_noinline void calculate_frame_offsets(PlanState *pstate)
static void eval_windowaggregates(WindowAggState *winstate)
static void update_frametailpos(WindowAggState *winstate)
static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2)
#define FRAMEOPTION_EXCLUDE_TIES
Definition: parsenodes.h:626
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:625
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:273
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1205
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1130
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:507
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1412

References WindowAggState::all_first, are_peers(), Assert(), begin_partition(), WindowAggState::buffer, calculate_frame_offsets(), castNode, CHECK_FOR_INTERRUPTS, WindowAggState::current_ptr, WindowAggState::currentgroup, WindowAggState::currentpos, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, ExprContext::ecxt_scantuple, elog, ERROR, eval_windowaggregates(), eval_windowfunction(), ExecClearTuple(), ExecCopySlot(), ExecProject(), ExecQual(), WindowAggState::framehead_ptr, WindowAggState::framehead_valid, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_valid, WindowAggState::groupheadpos, WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, i, InstrCountFiltered1, WindowAggState::more_partitions, WindowAggState::next_partition, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partition_spooled, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, ScanState::ps, PlanState::ps_ExprContext, PlanState::ps_ProjInfo, PlanState::qual, release_partition(), ResetExprContext, WindowAggState::runcondition, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::status, WindowAggState::temp_slot_2, WindowAggState::top_window, tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_trim(), unlikely, update_frameheadpos(), update_frametailpos(), update_grouptailpos(), WindowAggState::use_pass_through, WindowFuncExprState::wfuncno, WindowStatePerFuncData::wfuncstate, WINDOWAGG_DONE, WINDOWAGG_PASSTHROUGH, WINDOWAGG_PASSTHROUGH_STRICT, and WINDOWAGG_RUN.

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

static void finalize_windowaggregate ( WindowAggState winstate,
WindowStatePerFunc  perfuncstate,
WindowStatePerAgg  peraggstate,
Datum result,
bool *  isnull 
)
static

Definition at line 625 of file nodeWindowAgg.c.

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}
#define MakeExpandedObjectReadOnly(d, isnull, typlen)

References WindowStatePerAggData::aggcontext, ExprContext::ecxt_per_tuple_memory, WindowStatePerAggData::finalfn, WindowStatePerAggData::finalfn_oid, FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, LOCAL_FCINFO, MakeExpandedObjectReadOnly, MemoryContextSwitchTo(), WindowStatePerAggData::numFinalArgs, OidIsValid, ScanState::ps, PlanState::ps_ExprContext, WindowStatePerAggData::resulttypeLen, WindowAggState::ss, WindowStatePerAggData::transtypeLen, WindowStatePerAggData::transValue, WindowStatePerAggData::transValueIsNull, and WindowStatePerFuncData::winCollation.

Referenced by eval_windowaggregates().

◆ get_notnull_info()

static uint8 get_notnull_info ( WindowObject  winobj,
int64  pos,
int  argno 
)
static

Definition at line 3526 of file nodeWindowAgg.c.

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}
uint8_t uint8
Definition: c.h:541
static void grow_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_MASK
#define NN_SHIFT(pos)

References grow_notnull_info(), NN_MASK, NN_POS_TO_BYTES, NN_SHIFT, and WindowObjectData::notnull_info.

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3159 of file nodeWindowAgg.c.

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}
#define TextDatumGetCString(d)
Definition: builtins.h:98
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1754
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:3041
unsigned int Oid
Definition: postgres_ext.h:32

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

Referenced by initialize_peragg().

◆ gettuple_eval_partition()

static Datum gettuple_eval_partition ( WindowObject  winobj,
int  argno,
int64  abs_pos,
bool *  isnull,
bool *  isout 
)
static

Definition at line 3294 of file nodeWindowAgg.c.

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}
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299

References WindowObjectData::argstates, ExprContext::ecxt_outertuple, ExecEvalExpr(), list_nth(), ScanState::ps, PlanState::ps_ExprContext, WindowAggState::ss, WindowAggState::temp_slot_1, window_gettupleslot(), and WindowObjectData::winstate.

Referenced by WinGetFuncArgInPartition().

◆ grow_notnull_info()

static void grow_notnull_info ( WindowObject  winobj,
int64  pos,
int  argno 
)
static

Definition at line 3482 of file nodeWindowAgg.c.

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}
size_t Size
Definition: c.h:615
void * repalloc0(void *pointer, Size oldsize, Size size)
Definition: mcxt.c:1682
#define INIT_NOT_NULL_INFO_NUM
#define NN_BYTES_TO_POS(bytes)

References ExprContext::ecxt_per_query_memory, INIT_NOT_NULL_INFO_NUM, MemoryContextSwitchTo(), NN_BYTES_TO_POS, NN_POS_TO_BYTES, WindowObjectData::notnull_info, WindowObjectData::num_notnull_info, palloc0(), ScanState::ps, PlanState::ps_ExprContext, repalloc0(), WindowAggState::ss, and WindowObjectData::winstate.

Referenced by get_notnull_info(), and put_notnull_info().

◆ ignorenulls_getfuncarginframe()

static Datum ignorenulls_getfuncarginframe ( WindowObject  winobj,
int  argno,
int  relpos,
int  seektype,
bool  set_mark,
bool *  isnull,
bool *  isout 
)
static

Definition at line 3327 of file nodeWindowAgg.c.

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}
#define NN_UNKNOWN
static void put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull)
#define NN_NULL
static uint8 get_notnull_info(WindowObject winobj, int64 pos, int argno)
#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

References WindowObjectData::argstates, Assert(), ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), WindowAggState::frameheadpos, WindowAggState::frametailpos, get_notnull_info(), list_nth(), NN_NULL, NN_UNKNOWN, ScanState::ps, PlanState::ps_ExprContext, put_notnull_info(), row_is_in_frame(), WindowAggState::ss, WindowAggState::temp_slot_1, update_frameheadpos(), update_frametailpos(), window_gettupleslot(), WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, WINDOW_SEEK_TAIL, WindowObjectIsValid, WinSetMarkPosition(), and WindowObjectData::winstate.

Referenced by WinGetFuncArgInFrame().

◆ init_notnull_info()

static void init_notnull_info ( WindowObject  winobj,
WindowStatePerFunc  perfuncstate 
)
static

Definition at line 3464 of file nodeWindowAgg.c.

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}

References WindowObjectData::ignore_nulls, WindowObjectData::notnull_info, WindowObjectData::num_notnull_info, WindowStatePerFuncData::numArguments, palloc0(), and PARSER_IGNORE_NULLS.

Referenced by ExecInitWindowAgg().

◆ initialize_peragg()

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

Definition at line 2886 of file nodeWindowAgg.c.

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}
int16 AttrNumber
Definition: attnum.h:21
bool contain_subplans(Node *clause)
Definition: clauses.c:340
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
static Datum GetAggInitVal(Datum textInitVal, Oid transtype)
void build_aggregate_finalfn_expr(Oid *agg_input_types, int num_finalfn_inputs, Oid agg_state_type, Oid agg_result_type, Oid agg_input_collation, Oid finalfn_oid, Expr **finalfnexpr)
Definition: parse_agg.c:2287
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:2076
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:2179
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:614
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
#define InvalidOid
Definition: postgres_ext.h:37
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:305
List * args
Definition: primnodes.h:605
Oid winfnoid
Definition: primnodes.h:597
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

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 250 of file nodeWindowAgg.c.

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}

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

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ prepare_tuplestore()

static pg_noinline void prepare_tuplestore ( WindowAggState winstate)
static

Definition at line 1129 of file nodeWindowAgg.c.

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}
int work_mem
Definition: globals.c:131
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_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:371

References WindowAggState::agg_winobj, Assert(), WindowAggState::buffer, WindowAggState::current_ptr, EXEC_FLAG_BACKWARD, WindowAggState::framehead_ptr, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_START_CURRENT_ROW, FRAMEOPTION_START_OFFSET, FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::grouptail_ptr, i, WindowObjectData::markptr, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAgg::ordNumCols, WindowAggState::perfunc, WindowStatePerFuncData::plain_agg, PlanState::plan, ScanState::ps, WindowObjectData::readptr, WindowAggState::ss, tuplestore_alloc_read_pointer(), tuplestore_begin_heap(), tuplestore_set_eflags(), WindowStatePerFuncData::winobj, and work_mem.

Referenced by begin_partition().

◆ put_notnull_info()

static void put_notnull_info ( WindowObject  winobj,
int64  pos,
int  argno,
bool  isnull 
)
static

Definition at line 3547 of file nodeWindowAgg.c.

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}
long val
Definition: informix.c:689
#define NN_NOTNULL

References grow_notnull_info(), NN_MASK, NN_NOTNULL, NN_NULL, NN_POS_TO_BYTES, NN_SHIFT, WindowObjectData::notnull_info, and val.

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1436 of file nodeWindowAgg.c.

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}
void tuplestore_clear(Tuplestorestate *state)
Definition: tuplestore.c:430

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, i, WindowObjectData::localmem, MemoryContextReset(), WindowAggState::next_partition, WindowAggState::numaggs, WindowAggState::numfuncs, WindowAggState::partcontext, WindowAggState::partition_spooled, WindowAggState::peragg, WindowAggState::perfunc, tuplestore_clear(), and WindowStatePerFuncData::winobj.

Referenced by ExecEndWindowAgg(), ExecReScanWindowAgg(), and ExecWindowAgg().

◆ row_is_in_frame()

static int row_is_in_frame ( WindowObject  winobj,
int64  pos,
TupleTableSlot slot,
bool  fetch_tuple 
)
static

Definition at line 1486 of file nodeWindowAgg.c.

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}
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:624
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:621

References are_peers(), Assert(), WindowAggState::currentpos, DatumGetInt64(), WindowAggState::endOffsetValue, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, WindowAggState::frameOptions, WindowAggState::frametailpos, WindowAggState::groupheadpos, if(), WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, ScanState::ss_ScanTupleSlot, update_frameheadpos(), update_frametailpos(), update_grouptailpos(), window_gettupleslot(), and WindowObjectData::winstate.

Referenced by eval_windowaggregates(), ignorenulls_getfuncarginframe(), and WinGetFuncArgInFrame().

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1342 of file nodeWindowAgg.c.

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}
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:275
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1554

References Assert(), WindowAggState::buffer, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_per_query_memory, ExecCopySlot(), ExecProcNode(), ExecQualAndReset(), WindowAggState::first_part_slot, if(), MemoryContextSwitchTo(), WindowAggState::more_partitions, outerPlan, outerPlanState, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, WindowAggState::spooled_rows, WindowAggState::ss, WindowAggState::status, WindowAggState::tmpcontext, TupIsNull, tuplestore_in_memory(), tuplestore_puttupleslot(), WINDOWAGG_PASSTHROUGH, WINDOWAGG_PASSTHROUGH_STRICT, and WINDOWAGG_RUN.

Referenced by ExecWindowAgg(), update_frameheadpos(), update_frametailpos(), update_grouptailpos(), window_gettupleslot(), WinGetFuncArgInPartition(), and WinGetPartitionRowCount().

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1593 of file nodeWindowAgg.c.

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}
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1224
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:620
static Datum BoolGetDatum(bool X)
Definition: postgres.h:112
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:398

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

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

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1843 of file nodeWindowAgg.c.

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}

References are_peers(), Assert(), BoolGetDatum(), WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, DatumGetBool(), DatumGetInt64(), ExprContext::ecxt_per_query_memory, elog, WindowAggState::endInRangeFunc, WindowAggState::endOffsetValue, ERROR, ExecClearTuple(), ExecCopySlot(), FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_OFFSET, FRAMEOPTION_END_OFFSET_PRECEDING, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_GROUPS, FRAMEOPTION_RANGE, FRAMEOPTION_ROWS, WindowAggState::frameOptions, WindowAggState::frametail_ptr, WindowAggState::frametail_slot, WindowAggState::frametail_valid, WindowAggState::frametailgroup, WindowAggState::frametailpos, FunctionCall5Coll(), if(), WindowAggState::inRangeAsc, WindowAggState::inRangeColl, WindowAggState::inRangeNullsFirst, MemoryContextSwitchTo(), WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, slot_getattr(), spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_2, TupIsNull, tuplestore_gettupleslot(), and tuplestore_select_read_pointer().

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

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 2093 of file nodeWindowAgg.c.

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}

References are_peers(), Assert(), WindowAggState::buffer, WindowAggState::currentpos, ExprContext::ecxt_per_query_memory, ExecClearTuple(), WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, if(), MemoryContextSwitchTo(), WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, PlanState::ps_ExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowAggState::temp_slot_2, tuplestore_gettupleslot(), and tuplestore_select_read_pointer().

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

◆ WinCheckAndInitializeNullTreatment()

void WinCheckAndInitializeNullTreatment ( WindowObject  winobj,
bool  allowNullTreatment,
FunctionCallInfo  fcinfo 
)

Definition at line 3578 of file nodeWindowAgg.c.

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}
#define funcname
Definition: indent_codes.h:69
#define NO_NULLTREATMENT
Definition: primnodes.h:588
Oid fn_oid
Definition: fmgr.h:59
FmgrInfo * flinfo
Definition: fmgr.h:87

References Assert(), elog, ereport, errcode(), errmsg(), ERROR, FunctionCallInfoBaseData::flinfo, FmgrInfo::fn_oid, funcname, get_func_name(), WindowObjectData::ignore_nulls, IGNORE_NULLS, NO_NULLTREATMENT, PARSER_IGNORE_NULLS, and WindowObjectIsValid.

Referenced by leadlag_common(), window_cume_dist(), window_dense_rank(), window_first_value(), window_last_value(), window_nth_value(), window_ntile(), window_percent_rank(), window_rank(), and window_row_number().

◆ window_gettupleslot()

static bool window_gettupleslot ( WindowObject  winobj,
int64  pos,
TupleTableSlot slot 
)
static

Definition at line 3204 of file nodeWindowAgg.c.

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}
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1162
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1187

References Assert(), WindowAggState::buffer, CHECK_FOR_INTERRUPTS, ExprContext::ecxt_per_query_memory, elog, ERROR, MemoryContextSwitchTo(), ScanState::ps, PlanState::ps_ExprContext, WindowObjectData::readptr, WindowObjectData::seekpos, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, tuplestore_advance(), tuplestore_gettupleslot(), tuplestore_select_read_pointer(), tuplestore_skiptuples(), and WindowObjectData::winstate.

Referenced by eval_windowaggregates(), gettuple_eval_partition(), ignorenulls_getfuncarginframe(), row_is_in_frame(), WinGetFuncArgInFrame(), and WinRowsArePeers().

◆ WinGetCurrentPosition()

int64 WinGetCurrentPosition ( WindowObject  winobj)

◆ WinGetFuncArgCurrent()

Datum WinGetFuncArgCurrent ( WindowObject  winobj,
int  argno,
bool *  isnull 
)

Definition at line 4117 of file nodeWindowAgg.c.

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}

References WindowObjectData::argstates, Assert(), ExprContext::ecxt_outertuple, ExecEvalExpr(), list_nth(), ScanState::ps, PlanState::ps_ExprContext, WindowAggState::ss, ScanState::ss_ScanTupleSlot, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by leadlag_common(), window_nth_value(), and window_ntile().

◆ WinGetFuncArgInFrame()

Datum WinGetFuncArgInFrame ( WindowObject  winobj,
int  argno,
int  relpos,
int  seektype,
bool  set_mark,
bool *  isnull,
bool *  isout 
)

Definition at line 3918 of file nodeWindowAgg.c.

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}
#define Min(x, y)
Definition: c.h:1008
#define Max(x, y)
Definition: c.h:1002
static Datum ignorenulls_getfuncarginframe(WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)

References WindowObjectData::argstates, Assert(), WindowAggState::currentpos, ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), WindowAggState::frameheadpos, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, WindowAggState::frameOptions, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAggState::grouptailpos, WindowObjectData::ignore_nulls, IGNORE_NULLS, ignorenulls_getfuncarginframe(), list_nth(), Max, Min, ScanState::ps, PlanState::ps_ExprContext, row_is_in_frame(), WindowAggState::ss, WindowAggState::temp_slot_1, update_frameheadpos(), update_frametailpos(), update_grouptailpos(), window_gettupleslot(), WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, WINDOW_SEEK_TAIL, WindowObjectIsValid, WinSetMarkPosition(), and WindowObjectData::winstate.

Referenced by window_first_value(), window_last_value(), and window_nth_value().

◆ WinGetFuncArgInPartition()

Datum WinGetFuncArgInPartition ( WindowObject  winobj,
int  argno,
int  relpos,
int  seektype,
bool  set_mark,
bool *  isnull,
bool *  isout 
)

Definition at line 3751 of file nodeWindowAgg.c.

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}
static Datum gettuple_eval_partition(WindowObject winobj, int argno, int64 abs_pos, bool *isnull, bool *isout)

References Assert(), WindowAggState::currentpos, elog, ERROR, get_notnull_info(), gettuple_eval_partition(), WindowObjectData::ignore_nulls, IGNORE_NULLS, NN_NOTNULL, NN_NULL, put_notnull_info(), spool_tuples(), WindowAggState::spooled_rows, WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, WINDOW_SEEK_TAIL, WindowObjectIsValid, WinSetMarkPosition(), and WindowObjectData::winstate.

Referenced by leadlag_common().

◆ WinGetPartitionLocalMemory()

void * WinGetPartitionLocalMemory ( WindowObject  winobj,
Size  sz 
)

Definition at line 3611 of file nodeWindowAgg.c.

3612{
3613 Assert(WindowObjectIsValid(winobj));
3614 if (winobj->localmem == NULL)
3615 winobj->localmem =
3617 return winobj->localmem;
3618}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1263

References Assert(), WindowObjectData::localmem, MemoryContextAllocZero(), WindowAggState::partcontext, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), window_cume_dist(), window_dense_rank(), window_ntile(), window_percent_rank(), and window_rank().

◆ WinGetPartitionRowCount()

int64 WinGetPartitionRowCount ( WindowObject  winobj)

◆ WinRowsArePeers()

bool WinRowsArePeers ( WindowObject  winobj,
int64  pos1,
int64  pos2 
)

Definition at line 3694 of file nodeWindowAgg.c.

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}
#define INT64_FORMAT
Definition: c.h:561

References are_peers(), Assert(), elog, ERROR, ExecClearTuple(), if(), INT64_FORMAT, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, WindowAggState::temp_slot_1, WindowAggState::temp_slot_2, window_gettupleslot(), WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), and window_cume_dist().

◆ WinSetMarkPosition()

void WinSetMarkPosition ( WindowObject  winobj,
int64  markpos 
)

Definition at line 3659 of file nodeWindowAgg.c.

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}

References Assert(), WindowAggState::buffer, elog, ERROR, WindowObjectData::markpos, WindowObjectData::markptr, WindowObjectData::readptr, WindowObjectData::seekpos, tuplestore_select_read_pointer(), tuplestore_skiptuples(), WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by eval_windowaggregates(), ignorenulls_getfuncarginframe(), rank_up(), window_row_number(), WinGetFuncArgInFrame(), and WinGetFuncArgInPartition().