PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 "common/int.h"
#include "executor/executor.h"
#include "executor/instrument.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 "utils/tuplestore.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)
 
voidWinGetPartitionLocalMemory (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 239 of file nodeWindowAgg.c.

◆ NN_BYTES_TO_POS

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

Definition at line 245 of file nodeWindowAgg.c.

◆ NN_ITEM_PER_VAR

#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)

Definition at line 241 of file nodeWindowAgg.c.

◆ NN_MASK

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

Definition at line 238 of file nodeWindowAgg.c.

◆ NN_NOTNULL

#define NN_NOTNULL   0x02 /* NOT NULL */

Definition at line 237 of file nodeWindowAgg.c.

◆ NN_NULL

#define NN_NULL   0x01 /* NULL */

Definition at line 236 of file nodeWindowAgg.c.

◆ NN_POS_TO_BYTES

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

Definition at line 243 of file nodeWindowAgg.c.

◆ NN_SHIFT

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

Definition at line 247 of file nodeWindowAgg.c.

◆ NN_UNKNOWN

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

Definition at line 235 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 289 of file nodeWindowAgg.c.

292{
294 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
295 int numArguments = perfuncstate->numArguments;
296 Datum newVal;
297 ListCell *arg;
298 int i;
300 ExprContext *econtext = winstate->tmpcontext;
301 ExprState *filter = wfuncstate->aggfilter;
302
304
305 /* Skip anything FILTERed out */
306 if (filter)
307 {
308 bool isnull;
309 Datum res = ExecEvalExpr(filter, econtext, &isnull);
310
311 if (isnull || !DatumGetBool(res))
312 {
314 return;
315 }
316 }
317
318 /* We start from 1, since the 0th arg will be the transition value */
319 i = 1;
320 foreach(arg, wfuncstate->args)
321 {
323
324 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
325 &fcinfo->args[i].isnull);
326 i++;
327 }
328
329 if (peraggstate->transfn.fn_strict)
330 {
331 /*
332 * For a strict transfn, nothing happens when there's a NULL input; we
333 * just keep the prior transValue. Note transValueCount doesn't
334 * change either.
335 */
336 for (i = 1; i <= numArguments; i++)
337 {
338 if (fcinfo->args[i].isnull)
339 {
341 return;
342 }
343 }
344
345 /*
346 * For strict transition functions with initial value NULL we use the
347 * first non-NULL input as the initial state. (We already checked
348 * that the agg's input type is binary-compatible with its transtype,
349 * so straight copy here is OK.)
350 *
351 * We must copy the datum into aggcontext if it is pass-by-ref. We do
352 * not need to pfree the old transValue, since it's NULL.
353 */
354 if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
355 {
357 peraggstate->transValue = datumCopy(fcinfo->args[1].value,
358 peraggstate->transtypeByVal,
359 peraggstate->transtypeLen);
360 peraggstate->transValueIsNull = false;
361 peraggstate->transValueCount = 1;
363 return;
364 }
365
366 if (peraggstate->transValueIsNull)
367 {
368 /*
369 * Don't call a strict function with NULL inputs. Note it is
370 * possible to get here despite the above tests, if the transfn is
371 * strict *and* returned a NULL on a prior cycle. If that happens
372 * we will propagate the NULL all the way to the end. That can
373 * only happen if there's no inverse transition function, though,
374 * since we disallow transitions back to NULL when there is one.
375 */
377 Assert(!OidIsValid(peraggstate->invtransfn_oid));
378 return;
379 }
380 }
381
382 /*
383 * OK to call the transition function. Set winstate->curaggcontext while
384 * calling it, for possible use by AggCheckCallContext.
385 */
386 InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
387 numArguments + 1,
388 perfuncstate->winCollation,
389 (Node *) winstate, NULL);
390 fcinfo->args[0].value = peraggstate->transValue;
391 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
392 winstate->curaggcontext = peraggstate->aggcontext;
393 newVal = FunctionCallInvoke(fcinfo);
394 winstate->curaggcontext = NULL;
395
396 /*
397 * Moving-aggregate transition functions must not return null, see
398 * advance_windowaggregate_base().
399 */
400 if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
403 errmsg("moving-aggregate transition function must not return null")));
404
405 /*
406 * We must track the number of rows included in transValue, since to
407 * remove the last input, advance_windowaggregate_base() mustn't call the
408 * inverse transition function, but simply reset transValue back to its
409 * initial value.
410 */
411 peraggstate->transValueCount++;
412
413 /*
414 * If pass-by-ref datatype, must copy the new value into aggcontext and
415 * free the prior transValue. But if transfn returned a pointer to its
416 * first input, we don't need to do anything. Also, if transfn returned a
417 * pointer to a R/W expanded object that is already a child of the
418 * aggcontext, assume we can adopt that value without copying it. (See
419 * comments for ExecAggCopyTransValue, which this code duplicates.)
420 */
421 if (!peraggstate->transtypeByVal &&
422 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
423 {
424 if (!fcinfo->isnull)
425 {
428 false,
429 peraggstate->transtypeLen) &&
431 /* do nothing */ ;
432 else
433 newVal = datumCopy(newVal,
434 peraggstate->transtypeByVal,
435 peraggstate->transtypeLen);
436 }
437 if (!peraggstate->transValueIsNull)
438 {
440 false,
441 peraggstate->transtypeLen))
443 else
444 pfree(DatumGetPointer(peraggstate->transValue));
445 }
446 }
447
449 peraggstate->transValue = newVal;
450 peraggstate->transValueIsNull = fcinfo->isnull;
451}
#define Assert(condition)
Definition c.h:943
#define OidIsValid(objectId)
Definition c.h:858
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:403
ExpandedObjectHeader * DatumGetEOHP(Datum d)
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
int i
Definition isn.c:77
void pfree(void *pointer)
Definition mcxt.c:1619
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition mcxt.c:783
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#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:332
static int fb(int x)
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:295
Definition nodes.h:135
MemoryContext curaggcontext
Definition execnodes.h:2588
ExprContext * tmpcontext
Definition execnodes.h:2589
ExprState * aggfilter
Definition execnodes.h:961

References 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(), fb(), FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, lfirst, LOCAL_FCINFO, MemoryContextGetParent(), MemoryContextSwitchTo(), OidIsValid, pfree(), and WindowAggState::tmpcontext.

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 466 of file nodeWindowAgg.c.

469{
471 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
472 int numArguments = perfuncstate->numArguments;
473 Datum newVal;
474 ListCell *arg;
475 int i;
477 ExprContext *econtext = winstate->tmpcontext;
478 ExprState *filter = wfuncstate->aggfilter;
479
481
482 /* Skip anything FILTERed out */
483 if (filter)
484 {
485 bool isnull;
486 Datum res = ExecEvalExpr(filter, econtext, &isnull);
487
488 if (isnull || !DatumGetBool(res))
489 {
491 return true;
492 }
493 }
494
495 /* We start from 1, since the 0th arg will be the transition value */
496 i = 1;
497 foreach(arg, wfuncstate->args)
498 {
500
501 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
502 &fcinfo->args[i].isnull);
503 i++;
504 }
505
506 if (peraggstate->invtransfn.fn_strict)
507 {
508 /*
509 * For a strict (inv)transfn, nothing happens when there's a NULL
510 * input; we just keep the prior transValue. Note transValueCount
511 * doesn't change either.
512 */
513 for (i = 1; i <= numArguments; i++)
514 {
515 if (fcinfo->args[i].isnull)
516 {
518 return true;
519 }
520 }
521 }
522
523 /* There should still be an added but not yet removed value */
524 Assert(peraggstate->transValueCount > 0);
525
526 /*
527 * In moving-aggregate mode, the state must never be NULL, except possibly
528 * before any rows have been aggregated (which is surely not the case at
529 * this point). This restriction allows us to interpret a NULL result
530 * from the inverse function as meaning "sorry, can't do an inverse
531 * transition in this case". We already checked this in
532 * advance_windowaggregate, but just for safety, check again.
533 */
534 if (peraggstate->transValueIsNull)
535 elog(ERROR, "aggregate transition value is NULL before inverse transition");
536
537 /*
538 * We mustn't use the inverse transition function to remove the last
539 * input. Doing so would yield a non-NULL state, whereas we should be in
540 * the initial state afterwards which may very well be NULL. So instead,
541 * we simply re-initialize the aggregate in this case.
542 */
543 if (peraggstate->transValueCount == 1)
544 {
547 &winstate->perfunc[peraggstate->wfuncno],
549 return true;
550 }
551
552 /*
553 * OK to call the inverse transition function. Set
554 * winstate->curaggcontext while calling it, for possible use by
555 * AggCheckCallContext.
556 */
557 InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
558 numArguments + 1,
559 perfuncstate->winCollation,
560 (Node *) winstate, NULL);
561 fcinfo->args[0].value = peraggstate->transValue;
562 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
563 winstate->curaggcontext = peraggstate->aggcontext;
564 newVal = FunctionCallInvoke(fcinfo);
565 winstate->curaggcontext = NULL;
566
567 /*
568 * If the function returns NULL, report failure, forcing a restart.
569 */
570 if (fcinfo->isnull)
571 {
573 return false;
574 }
575
576 /* Update number of rows included in transValue */
577 peraggstate->transValueCount--;
578
579 /*
580 * If pass-by-ref datatype, must copy the new value into aggcontext and
581 * free the prior transValue. But if invtransfn returned a pointer to its
582 * first input, we don't need to do anything. Also, if invtransfn
583 * returned a pointer to a R/W expanded object that is already a child of
584 * the aggcontext, assume we can adopt that value without copying it. (See
585 * comments for ExecAggCopyTransValue, which this code duplicates.)
586 *
587 * Note: the checks for null values here will never fire, but it seems
588 * best to have this stanza look just like advance_windowaggregate.
589 */
590 if (!peraggstate->transtypeByVal &&
591 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
592 {
593 if (!fcinfo->isnull)
594 {
597 false,
598 peraggstate->transtypeLen) &&
600 /* do nothing */ ;
601 else
602 newVal = datumCopy(newVal,
603 peraggstate->transtypeByVal,
604 peraggstate->transtypeLen);
605 }
606 if (!peraggstate->transValueIsNull)
607 {
609 false,
610 peraggstate->transtypeLen))
612 else
613 pfree(DatumGetPointer(peraggstate->transValue));
614 }
615 }
616
618 peraggstate->transValue = newVal;
619 peraggstate->transValueIsNull = fcinfo->isnull;
620
621 return true;
622}
#define elog(elevel,...)
Definition elog.h:228
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
WindowStatePerFunc perfunc
Definition execnodes.h:2536

References WindowFuncExprState::aggfilter, arg, WindowFuncExprState::args, Assert, WindowAggState::curaggcontext, CurrentMemoryContext, datumCopy(), DatumGetBool(), DatumGetEOHP(), DatumGetPointer(), DatumIsReadWriteExpandedObject, DeleteExpandedObject(), ExprContext::ecxt_per_tuple_memory, elog, ERROR, ExecEvalExpr(), fb(), FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, initialize_windowaggregate(), lfirst, LOCAL_FCINFO, MemoryContextGetParent(), MemoryContextSwitchTo(), WindowAggState::perfunc, pfree(), and WindowAggState::tmpcontext.

Referenced by eval_windowaggregates().

◆ are_peers()

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

Definition at line 3232 of file nodeWindowAgg.c.

3234{
3235 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3236 ExprContext *econtext = winstate->tmpcontext;
3237
3238 /* If no ORDER BY, all rows are peers with each other */
3239 if (node->ordNumCols == 0)
3240 return true;
3241
3242 econtext->ecxt_outertuple = slot1;
3243 econtext->ecxt_innertuple = slot2;
3244 return ExecQualAndReset(winstate->ordEqfunction, econtext);
3245}
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition executor.h:556
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:289
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:291
Plan * plan
Definition execnodes.h:1201
PlanState ps
Definition execnodes.h:1659
ScanState ss
Definition execnodes.h:2529
ExprState * ordEqfunction
Definition execnodes.h:2539

References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecQualAndReset(), fb(), 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 1241 of file nodeWindowAgg.c.

1242{
1243 PlanState *outerPlan = outerPlanState(winstate);
1244 int numfuncs = winstate->numfuncs;
1245
1246 winstate->partition_spooled = false;
1247 winstate->framehead_valid = false;
1248 winstate->frametail_valid = false;
1249 winstate->grouptail_valid = false;
1250 winstate->spooled_rows = 0;
1251 winstate->currentpos = 0;
1252 winstate->frameheadpos = 0;
1253 winstate->frametailpos = 0;
1254 winstate->currentgroup = 0;
1255 winstate->frameheadgroup = 0;
1256 winstate->frametailgroup = 0;
1257 winstate->groupheadpos = 0;
1258 winstate->grouptailpos = -1; /* see update_grouptailpos */
1259 ExecClearTuple(winstate->agg_row_slot);
1260 if (winstate->framehead_slot)
1261 ExecClearTuple(winstate->framehead_slot);
1262 if (winstate->frametail_slot)
1263 ExecClearTuple(winstate->frametail_slot);
1264
1265 /*
1266 * If this is the very first partition, we need to fetch the first input
1267 * row to store in first_part_slot.
1268 */
1269 if (TupIsNull(winstate->first_part_slot))
1270 {
1272
1273 if (!TupIsNull(outerslot))
1275 else
1276 {
1277 /* outer plan is empty, so we have nothing to do */
1278 winstate->partition_spooled = true;
1279 winstate->more_partitions = false;
1280 return;
1281 }
1282 }
1283
1284 /* Create new tuplestore if not done already. */
1285 if (unlikely(winstate->buffer == NULL))
1286 prepare_tuplestore(winstate);
1287
1288 winstate->next_partition = false;
1289
1290 if (winstate->numaggs > 0)
1291 {
1292 WindowObject agg_winobj = winstate->agg_winobj;
1293
1294 /* reset mark and see positions for aggregate functions */
1295 agg_winobj->markpos = -1;
1296 agg_winobj->seekpos = -1;
1297
1298 /* Also reset the row counters for aggregates */
1299 winstate->aggregatedbase = 0;
1300 winstate->aggregatedupto = 0;
1301 }
1302
1303 /* reset mark and seek positions for each real window function */
1304 for (int i = 0; i < numfuncs; i++)
1305 {
1306 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1307
1308 if (!perfuncstate->plain_agg)
1309 {
1310 WindowObject winobj = perfuncstate->winobj;
1311
1312 winobj->markpos = -1;
1313 winobj->seekpos = -1;
1314
1315 /* reset null map */
1316 if (winobj->ignore_nulls == IGNORE_NULLS ||
1318 {
1319 int numargs = perfuncstate->numArguments;
1320
1321 for (int j = 0; j < numargs; j++)
1322 {
1323 int n = winobj->num_notnull_info[j];
1324
1325 if (n > 0)
1326 memset(winobj->notnull_info[j], 0,
1327 NN_POS_TO_BYTES(n));
1328 }
1329 }
1330 }
1331 }
1332
1333 /*
1334 * Store the first tuple into the tuplestore (it's always available now;
1335 * we either read it above, or saved it at the end of previous partition)
1336 */
1337 tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1338 winstate->spooled_rows++;
1339}
#define unlikely(x)
Definition c.h:438
#define outerPlanState(node)
Definition execnodes.h:1299
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition executor.h:322
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:267
#define PARSER_IGNORE_NULLS
Definition primnodes.h:590
#define IGNORE_NULLS
Definition primnodes.h:592
int64 aggregatedbase
Definition execnodes.h:2551
int64 frametailgroup
Definition execnodes.h:2582
int64 frameheadgroup
Definition execnodes.h:2581
TupleTableSlot * framehead_slot
Definition execnodes.h:2606
bool partition_spooled
Definition execnodes.h:2592
TupleTableSlot * frametail_slot
Definition execnodes.h:2607
Tuplestorestate * buffer
Definition execnodes.h:2540
TupleTableSlot * agg_row_slot
Definition execnodes.h:2610
struct WindowObjectData * agg_winobj
Definition execnodes.h:2550
TupleTableSlot * first_part_slot
Definition execnodes.h:2604
int64 aggregatedupto
Definition execnodes.h:2552
int64 * num_notnull_info
uint8 ** notnull_info
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition tuplestore.c:743
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
#define TupIsNull(slot)
Definition tuptable.h:325
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition tuptable.h:544

References WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggregatedbase, WindowAggState::aggregatedupto, WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, ExecClearTuple(), ExecCopySlot(), ExecProcNode(), fb(), 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, WindowAggState::numfuncs, outerPlan, outerPlanState, PARSER_IGNORE_NULLS, WindowAggState::partition_spooled, WindowAggState::perfunc, prepare_tuplestore(), WindowObjectData::seekpos, WindowAggState::spooled_rows, TupIsNull, tuplestore_puttupleslot(), and unlikely.

Referenced by ExecWindowAgg().

◆ calculate_frame_offsets()

static pg_noinline void calculate_frame_offsets ( PlanState pstate)
static

Definition at line 2200 of file nodeWindowAgg.c.

2201{
2202 WindowAggState *winstate = castNode(WindowAggState, pstate);
2203 ExprContext *econtext;
2204 int frameOptions = winstate->frameOptions;
2205 Datum value;
2206 bool isnull;
2207 int16 len;
2208 bool byval;
2209
2210 /* Ensure we've not been called before for this scan */
2211 Assert(winstate->all_first);
2212
2213 econtext = winstate->ss.ps.ps_ExprContext;
2214
2215 if (frameOptions & FRAMEOPTION_START_OFFSET)
2216 {
2217 Assert(winstate->startOffset != NULL);
2219 econtext,
2220 &isnull);
2221 if (isnull)
2222 ereport(ERROR,
2224 errmsg("frame starting offset must not be null")));
2225 /* copy value into query-lifespan context */
2227 &len,
2228 &byval);
2229 winstate->startOffsetValue = datumCopy(value, byval, len);
2230 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2231 {
2232 /* value is known to be int8 */
2233 int64 offset = DatumGetInt64(value);
2234
2235 if (offset < 0)
2236 ereport(ERROR,
2238 errmsg("frame starting offset must not be negative")));
2239 }
2240 }
2241
2242 if (frameOptions & FRAMEOPTION_END_OFFSET)
2243 {
2244 Assert(winstate->endOffset != NULL);
2246 econtext,
2247 &isnull);
2248 if (isnull)
2249 ereport(ERROR,
2251 errmsg("frame ending offset must not be null")));
2252 /* copy value into query-lifespan context */
2253 get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2254 &len,
2255 &byval);
2256 winstate->endOffsetValue = datumCopy(value, byval, len);
2257 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2258 {
2259 /* value is known to be int8 */
2260 int64 offset = DatumGetInt64(value);
2261
2262 if (offset < 0)
2263 ereport(ERROR,
2265 errmsg("frame ending offset must not be negative")));
2266 }
2267 }
2268 winstate->all_first = false;
2269}
int64_t int64
Definition c.h:621
int16_t int16
Definition c.h:619
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:446
static struct @177 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2482
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition nodes.h:182
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:633
#define FRAMEOPTION_START_OFFSET
Definition parsenodes.h:631
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:615
#define FRAMEOPTION_ROWS
Definition parsenodes.h:614
const void size_t len
static int64 DatumGetInt64(Datum X)
Definition postgres.h:416
Expr * expr
Definition execnodes.h:132
ExprContext * ps_ExprContext
Definition execnodes.h:1242
ExprState * endOffset
Definition execnodes.h:2557
Datum startOffsetValue
Definition execnodes.h:2558
Datum endOffsetValue
Definition execnodes.h:2559
ExprState * startOffset
Definition execnodes.h:2556

References WindowAggState::all_first, Assert, castNode, datumCopy(), DatumGetInt64(), WindowAggState::endOffset, WindowAggState::endOffsetValue, ereport, errcode(), errmsg, ERROR, ExecEvalExprSwitchContext(), ExprState::expr, exprType(), fb(), 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 710 of file nodeWindowAgg.c.

711{
713 int wfuncno,
714 numaggs,
716 i;
719 ExprContext *econtext;
720 WindowObject agg_winobj;
721 TupleTableSlot *agg_row_slot;
723
724 numaggs = winstate->numaggs;
725 if (numaggs == 0)
726 return; /* nothing to do */
727
728 /* final output execution is in ps_ExprContext */
729 econtext = winstate->ss.ps.ps_ExprContext;
730 agg_winobj = winstate->agg_winobj;
731 agg_row_slot = winstate->agg_row_slot;
732 temp_slot = winstate->temp_slot_1;
733
734 /*
735 * If the window's frame start clause is UNBOUNDED_PRECEDING and no
736 * exclusion clause is specified, then the window frame consists of a
737 * contiguous group of rows extending forward from the start of the
738 * partition, and rows only enter the frame, never exit it, as the current
739 * row advances forward. This makes it possible to use an incremental
740 * strategy for evaluating aggregates: we run the transition function for
741 * each row added to the frame, and run the final function whenever we
742 * need the current aggregate value. This is considerably more efficient
743 * than the naive approach of re-running the entire aggregate calculation
744 * for each current row. It does assume that the final function doesn't
745 * damage the running transition value, but we have the same assumption in
746 * nodeAgg.c too (when it rescans an existing hash table).
747 *
748 * If the frame start does sometimes move, we can still optimize as above
749 * whenever successive rows share the same frame head, but if the frame
750 * head moves beyond the previous head we try to remove those rows using
751 * the aggregate's inverse transition function. This function restores
752 * the aggregate's current state to what it would be if the removed row
753 * had never been aggregated in the first place. Inverse transition
754 * functions may optionally return NULL, indicating that the function was
755 * unable to remove the tuple from aggregation. If this happens, or if
756 * the aggregate doesn't have an inverse transition function at all, we
757 * must perform the aggregation all over again for all tuples within the
758 * new frame boundaries.
759 *
760 * If there's any exclusion clause, then we may have to aggregate over a
761 * non-contiguous set of rows, so we punt and recalculate for every row.
762 * (For some frame end choices, it might be that the frame is always
763 * contiguous anyway, but that's an optimization to investigate later.)
764 *
765 * In many common cases, multiple rows share the same frame and hence the
766 * same aggregate value. (In particular, if there's no ORDER BY in a RANGE
767 * window, then all rows are peers and so they all have window frame equal
768 * to the whole partition.) We optimize such cases by calculating the
769 * aggregate value once when we reach the first row of a peer group, and
770 * then returning the saved value for all subsequent rows.
771 *
772 * 'aggregatedupto' keeps track of the first row that has not yet been
773 * accumulated into the aggregate transition values. Whenever we start a
774 * new peer group, we accumulate forward to the end of the peer group.
775 */
776
777 /*
778 * First, update the frame head position.
779 *
780 * The frame head should never move backwards, and the code below wouldn't
781 * cope if it did, so for safety we complain if it does.
782 */
783 update_frameheadpos(winstate);
784 if (winstate->frameheadpos < winstate->aggregatedbase)
785 elog(ERROR, "window frame head moved backward");
786
787 /*
788 * If the frame didn't change compared to the previous row, we can re-use
789 * the result values that were previously saved at the bottom of this
790 * function. Since we don't know the current frame's end yet, this is not
791 * possible to check for fully. But if the frame end mode is UNBOUNDED
792 * FOLLOWING or CURRENT ROW, no exclusion clause is specified, and the
793 * current row lies within the previous row's frame, then the two frames'
794 * ends must coincide. Note that on the first row aggregatedbase ==
795 * aggregatedupto, meaning this test must fail, so we don't need to check
796 * the "there was no previous row" case explicitly here.
797 */
798 if (winstate->aggregatedbase == winstate->frameheadpos &&
801 !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
802 winstate->aggregatedbase <= winstate->currentpos &&
803 winstate->aggregatedupto > winstate->currentpos)
804 {
805 for (i = 0; i < numaggs; i++)
806 {
807 peraggstate = &winstate->peragg[i];
808 wfuncno = peraggstate->wfuncno;
809 econtext->ecxt_aggvalues[wfuncno] = peraggstate->resultValue;
810 econtext->ecxt_aggnulls[wfuncno] = peraggstate->resultValueIsNull;
811 }
812 return;
813 }
814
815 /*----------
816 * Initialize restart flags.
817 *
818 * We restart the aggregation:
819 * - if we're processing the first row in the partition, or
820 * - if the frame's head moved and we cannot use an inverse
821 * transition function, or
822 * - we have an EXCLUSION clause, or
823 * - if the new frame doesn't overlap the old one
824 *
825 * Note that we don't strictly need to restart in the last case, but if
826 * we're going to remove all rows from the aggregation anyway, a restart
827 * surely is faster.
828 *----------
829 */
830 numaggs_restart = 0;
831 for (i = 0; i < numaggs; i++)
832 {
833 peraggstate = &winstate->peragg[i];
834 if (winstate->currentpos == 0 ||
835 (winstate->aggregatedbase != winstate->frameheadpos &&
836 !OidIsValid(peraggstate->invtransfn_oid)) ||
837 (winstate->frameOptions & FRAMEOPTION_EXCLUSION) ||
838 winstate->aggregatedupto <= winstate->frameheadpos)
839 {
840 peraggstate->restart = true;
842 }
843 else
844 peraggstate->restart = false;
845 }
846
847 /*
848 * If we have any possibly-moving aggregates, attempt to advance
849 * aggregatedbase to match the frame's head by removing input rows that
850 * fell off the top of the frame from the aggregations. This can fail,
851 * i.e. advance_windowaggregate_base() can return false, in which case
852 * we'll restart that aggregate below.
853 */
854 while (numaggs_restart < numaggs &&
855 winstate->aggregatedbase < winstate->frameheadpos)
856 {
857 /*
858 * Fetch the next tuple of those being removed. This should never fail
859 * as we should have been here before.
860 */
861 if (!window_gettupleslot(agg_winobj, winstate->aggregatedbase,
862 temp_slot))
863 elog(ERROR, "could not re-fetch previously fetched frame row");
864
865 /* Set tuple context for evaluation of aggregate arguments */
867
868 /*
869 * Perform the inverse transition for each aggregate function in the
870 * window, unless it has already been marked as needing a restart.
871 */
872 for (i = 0; i < numaggs; i++)
873 {
874 bool ok;
875
876 peraggstate = &winstate->peragg[i];
877 if (peraggstate->restart)
878 continue;
879
880 wfuncno = peraggstate->wfuncno;
882 &winstate->perfunc[wfuncno],
884 if (!ok)
885 {
886 /* Inverse transition function has failed, must restart */
887 peraggstate->restart = true;
889 }
890 }
891
892 /* Reset per-input-tuple context after each tuple */
893 ResetExprContext(winstate->tmpcontext);
894
895 /* And advance the aggregated-row state */
896 winstate->aggregatedbase++;
898 }
899
900 /*
901 * If we successfully advanced the base rows of all the aggregates,
902 * aggregatedbase now equals frameheadpos; but if we failed for any, we
903 * must forcibly update aggregatedbase.
904 */
905 winstate->aggregatedbase = winstate->frameheadpos;
906
907 /*
908 * If we created a mark pointer for aggregates, keep it pushed up to frame
909 * head, so that tuplestore can discard unnecessary rows.
910 */
911 if (agg_winobj->markptr >= 0)
912 WinSetMarkPosition(agg_winobj, winstate->frameheadpos);
913
914 /*
915 * Now restart the aggregates that require it.
916 *
917 * We assume that aggregates using the shared context always restart if
918 * *any* aggregate restarts, and we may thus clean up the shared
919 * aggcontext if that is the case. Private aggcontexts are reset by
920 * initialize_windowaggregate() if their owning aggregate restarts. If we
921 * aren't restarting an aggregate, we need to free any previously saved
922 * result for it, else we'll leak memory.
923 */
924 if (numaggs_restart > 0)
926 for (i = 0; i < numaggs; i++)
927 {
928 peraggstate = &winstate->peragg[i];
929
930 /* Aggregates using the shared ctx must restart if *any* agg does */
931 Assert(peraggstate->aggcontext != winstate->aggcontext ||
932 numaggs_restart == 0 ||
933 peraggstate->restart);
934
935 if (peraggstate->restart)
936 {
937 wfuncno = peraggstate->wfuncno;
939 &winstate->perfunc[wfuncno],
941 }
942 else if (!peraggstate->resultValueIsNull)
943 {
944 if (!peraggstate->resulttypeByVal)
945 pfree(DatumGetPointer(peraggstate->resultValue));
946 peraggstate->resultValue = (Datum) 0;
947 peraggstate->resultValueIsNull = true;
948 }
949 }
950
951 /*
952 * Non-restarted aggregates now contain the rows between aggregatedbase
953 * (i.e., frameheadpos) and aggregatedupto, while restarted aggregates
954 * contain no rows. If there are any restarted aggregates, we must thus
955 * begin aggregating anew at frameheadpos, otherwise we may simply
956 * continue at aggregatedupto. We must remember the old value of
957 * aggregatedupto to know how long to skip advancing non-restarted
958 * aggregates. If we modify aggregatedupto, we must also clear
959 * agg_row_slot, per the loop invariant below.
960 */
962 if (numaggs_restart > 0 &&
963 winstate->aggregatedupto != winstate->frameheadpos)
964 {
965 winstate->aggregatedupto = winstate->frameheadpos;
966 ExecClearTuple(agg_row_slot);
967 }
968
969 /*
970 * Advance until we reach a row not in frame (or end of partition).
971 *
972 * Note the loop invariant: agg_row_slot is either empty or holds the row
973 * at position aggregatedupto. We advance aggregatedupto after processing
974 * a row.
975 */
976 for (;;)
977 {
978 int ret;
979
980 /* Fetch next row if we didn't already */
981 if (TupIsNull(agg_row_slot))
982 {
983 if (!window_gettupleslot(agg_winobj, winstate->aggregatedupto,
984 agg_row_slot))
985 break; /* must be end of partition */
986 }
987
988 /*
989 * Exit loop if no more rows can be in frame. Skip aggregation if
990 * current row is not in frame but there might be more in the frame.
991 */
992 ret = row_is_in_frame(agg_winobj, winstate->aggregatedupto,
993 agg_row_slot, false);
994 if (ret < 0)
995 break;
996 if (ret == 0)
997 goto next_tuple;
998
999 /* Set tuple context for evaluation of aggregate arguments */
1000 winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
1001
1002 /* Accumulate row into the aggregates */
1003 for (i = 0; i < numaggs; i++)
1004 {
1005 peraggstate = &winstate->peragg[i];
1006
1007 /* Non-restarted aggs skip until aggregatedupto_nonrestarted */
1008 if (!peraggstate->restart &&
1010 continue;
1011
1012 wfuncno = peraggstate->wfuncno;
1013 advance_windowaggregate(winstate,
1014 &winstate->perfunc[wfuncno],
1015 peraggstate);
1016 }
1017
1018next_tuple:
1019 /* Reset per-input-tuple context after each tuple */
1020 ResetExprContext(winstate->tmpcontext);
1021
1022 /* And advance the aggregated-row state */
1023 winstate->aggregatedupto++;
1024 ExecClearTuple(agg_row_slot);
1025 }
1026
1027 /* The frame's end is not supposed to move backwards, ever */
1029
1030 /*
1031 * finalize aggregates and fill result/isnull fields.
1032 */
1033 for (i = 0; i < numaggs; i++)
1034 {
1035 Datum *result;
1036 bool *isnull;
1037
1038 peraggstate = &winstate->peragg[i];
1039 wfuncno = peraggstate->wfuncno;
1040 result = &econtext->ecxt_aggvalues[wfuncno];
1041 isnull = &econtext->ecxt_aggnulls[wfuncno];
1042 finalize_windowaggregate(winstate,
1043 &winstate->perfunc[wfuncno],
1045 result, isnull);
1046
1047 /*
1048 * save the result in case next row shares the same frame.
1049 *
1050 * XXX in some framing modes, eg ROWS/END_CURRENT_ROW, we can know in
1051 * advance that the next row can't possibly share the same frame. Is
1052 * it worth detecting that and skipping this code?
1053 */
1054 if (!peraggstate->resulttypeByVal && !*isnull)
1055 {
1057 peraggstate->resultValue =
1059 peraggstate->resulttypeByVal,
1060 peraggstate->resulttypeLen);
1062 }
1063 else
1064 {
1065 peraggstate->resultValue = *result;
1066 }
1067 peraggstate->resultValueIsNull = *isnull;
1068 }
1069}
uint32 result
#define ResetExprContext(econtext)
Definition executor.h:661
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
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:622
#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING
Definition parsenodes.h:620
#define FRAMEOPTION_EXCLUSION
Definition parsenodes.h:635
Datum * ecxt_aggvalues
Definition execnodes.h:306
bool * ecxt_aggnulls
Definition execnodes.h:308
MemoryContext aggcontext
Definition execnodes.h:2587
WindowStatePerAgg peragg
Definition execnodes.h:2537
TupleTableSlot * temp_slot_1
Definition execnodes.h:2611

References advance_windowaggregate(), advance_windowaggregate_base(), WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, WindowAggState::aggregatedbase, WindowAggState::aggregatedupto, Assert, WindowAggState::currentpos, datumCopy(), DatumGetPointer(), ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExprContext::ecxt_outertuple, elog, ERROR, ExecClearTuple(), fb(), finalize_windowaggregate(), WindowAggState::frameheadpos, FRAMEOPTION_END_CURRENT_ROW, FRAMEOPTION_END_UNBOUNDED_FOLLOWING, FRAMEOPTION_EXCLUSION, WindowAggState::frameOptions, i, initialize_windowaggregate(), WindowObjectData::markptr, MemoryContextReset(), MemoryContextSwitchTo(), WindowAggState::numaggs, OidIsValid, WindowAggState::peragg, WindowAggState::perfunc, pfree(), ScanState::ps, PlanState::ps_ExprContext, ResetExprContext, WindowStatePerAggData::restart, result, 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 1081 of file nodeWindowAgg.c.

1083{
1084 LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1086
1088
1089 /*
1090 * We don't pass any normal arguments to a window function, but we do pass
1091 * it the number of arguments, in order to permit window function
1092 * implementations to support varying numbers of arguments. The real info
1093 * goes through the WindowObject, which is passed via fcinfo->context.
1094 */
1095 InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1096 perfuncstate->numArguments,
1097 perfuncstate->winCollation,
1098 (Node *) perfuncstate->winobj, NULL);
1099 /* Just in case, make all the regular argument slots be null */
1100 for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1101 fcinfo->args[argno].isnull = true;
1102 /* Window functions don't have a current aggregate context, either */
1103 winstate->curaggcontext = NULL;
1104
1105 *result = FunctionCallInvoke(fcinfo);
1106 *isnull = fcinfo->isnull;
1107
1108 /*
1109 * The window function might have returned a pass-by-ref result that's
1110 * just a pointer into one of the WindowObject's temporary slots. That's
1111 * not a problem if it's the only window function using the WindowObject;
1112 * but if there's more than one function, we'd better copy the result to
1113 * ensure it's not clobbered by later window functions.
1114 */
1115 if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1116 winstate->numfuncs > 1)
1118 perfuncstate->resulttypeByVal,
1119 perfuncstate->resulttypeLen);
1120
1122}

References WindowAggState::curaggcontext, datumCopy(), ExprContext::ecxt_per_tuple_memory, fb(), FUNC_MAX_ARGS, FunctionCallInvoke, InitFunctionCallInfoData, LOCAL_FCINFO, MemoryContextSwitchTo(), WindowAggState::numfuncs, ScanState::ps, PlanState::ps_ExprContext, result, and WindowAggState::ss.

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2862 of file nodeWindowAgg.c.

2863{
2865 int i;
2866
2867 if (node->buffer != NULL)
2868 {
2869 tuplestore_end(node->buffer);
2870
2871 /* nullify so that release_partition skips the tuplestore_clear() */
2872 node->buffer = NULL;
2873 }
2874
2875 release_partition(node);
2876
2877 for (i = 0; i < node->numaggs; i++)
2878 {
2879 if (node->peragg[i].aggcontext != node->aggcontext)
2881 }
2884
2885 pfree(node->perfunc);
2886 pfree(node->peragg);
2887
2888 outerPlan = outerPlanState(node);
2890}
void ExecEndNode(PlanState *node)
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition execnodes.h:2586
MemoryContext aggcontext
void tuplestore_end(Tuplestorestate *state)
Definition tuplestore.c:493

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, ExecEndNode(), fb(), 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 2549 of file nodeWindowAgg.c.

2550{
2551 WindowAggState *winstate;
2552 Plan *outerPlan;
2553 ExprContext *econtext;
2554 ExprContext *tmpcontext;
2555 WindowStatePerFunc perfunc;
2556 WindowStatePerAgg peragg;
2557 int frameOptions = node->frameOptions;
2558 int numfuncs,
2559 wfuncno,
2560 numaggs,
2561 aggno;
2563 ListCell *l;
2564
2565 /* check for unsupported flags */
2566 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2567
2568 /*
2569 * create state structure
2570 */
2571 winstate = makeNode(WindowAggState);
2572 winstate->ss.ps.plan = (Plan *) node;
2573 winstate->ss.ps.state = estate;
2574 winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2575
2576 /* copy frame options to state node for easy access */
2577 winstate->frameOptions = frameOptions;
2578
2579 /*
2580 * Create expression contexts. We need two, one for per-input-tuple
2581 * processing and one for per-output-tuple processing. We cheat a little
2582 * by using ExecAssignExprContext() to build both.
2583 */
2584 ExecAssignExprContext(estate, &winstate->ss.ps);
2585 tmpcontext = winstate->ss.ps.ps_ExprContext;
2586 winstate->tmpcontext = tmpcontext;
2587 ExecAssignExprContext(estate, &winstate->ss.ps);
2588
2589 /* Create long-lived context for storage of partition-local memory etc */
2590 winstate->partcontext =
2592 "WindowAgg Partition",
2594
2595 /*
2596 * Create mid-lived context for aggregate trans values etc.
2597 *
2598 * Note that moving aggregates each use their own private context, not
2599 * this one.
2600 */
2601 winstate->aggcontext =
2603 "WindowAgg Aggregates",
2605
2606 /* Only the top-level WindowAgg may have a qual */
2607 Assert(node->plan.qual == NIL || node->topWindow);
2608
2609 /* Initialize the qual */
2610 winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2611 (PlanState *) winstate);
2612
2613 /*
2614 * Setup the run condition, if we received one from the query planner.
2615 * When set, this may allow us to move into pass-through mode so that we
2616 * don't have to perform any further evaluation of WindowFuncs in the
2617 * current partition or possibly stop returning tuples altogether when all
2618 * tuples are in the same partition.
2619 */
2620 winstate->runcondition = ExecInitQual(node->runCondition,
2621 (PlanState *) winstate);
2622
2623 /*
2624 * When we're not the top-level WindowAgg node or we are but have a
2625 * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2626 * modes when the runCondition becomes false.
2627 */
2628 winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2629
2630 /* remember if we're the top-window or we are below the top-window */
2631 winstate->top_window = node->topWindow;
2632
2633 /*
2634 * initialize child nodes
2635 */
2636 outerPlan = outerPlan(node);
2637 outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2638
2639 /*
2640 * initialize source tuple type (which is also the tuple type that we'll
2641 * store in the tuplestore and use in all our working slots).
2642 */
2645
2646 /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2647 winstate->ss.ps.outeropsset = true;
2648 winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2649 winstate->ss.ps.outeropsfixed = true;
2650
2651 /*
2652 * tuple table initialization
2653 */
2656 winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2658 winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2660 winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2662
2663 /*
2664 * create frame head and tail slots only if needed (must create slots in
2665 * exactly the same cases that update_frameheadpos and update_frametailpos
2666 * need them)
2667 */
2668 winstate->framehead_slot = winstate->frametail_slot = NULL;
2669
2670 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2671 {
2672 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2673 node->ordNumCols != 0) ||
2674 (frameOptions & FRAMEOPTION_START_OFFSET))
2677 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2678 node->ordNumCols != 0) ||
2679 (frameOptions & FRAMEOPTION_END_OFFSET))
2682 }
2683
2684 /*
2685 * Initialize result slot, type and projection.
2686 */
2688 ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2689
2690 /* Set up data for comparing tuples */
2691 if (node->partNumCols > 0)
2692 winstate->partEqfunction =
2694 node->partNumCols,
2695 node->partColIdx,
2696 node->partOperators,
2697 node->partCollations,
2698 &winstate->ss.ps);
2699
2700 if (node->ordNumCols > 0)
2701 winstate->ordEqfunction =
2703 node->ordNumCols,
2704 node->ordColIdx,
2705 node->ordOperators,
2706 node->ordCollations,
2707 &winstate->ss.ps);
2708
2709 /*
2710 * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2711 */
2712 numfuncs = winstate->numfuncs;
2713 numaggs = winstate->numaggs;
2714 econtext = winstate->ss.ps.ps_ExprContext;
2715 econtext->ecxt_aggvalues = palloc0_array(Datum, numfuncs);
2716 econtext->ecxt_aggnulls = palloc0_array(bool, numfuncs);
2717
2718 /*
2719 * allocate per-wfunc/per-agg state information.
2720 */
2721 perfunc = palloc0_array(WindowStatePerFuncData, numfuncs);
2722 peragg = palloc0_array(WindowStatePerAggData, numaggs);
2723 winstate->perfunc = perfunc;
2724 winstate->peragg = peragg;
2725
2726 wfuncno = -1;
2727 aggno = -1;
2728 foreach(l, winstate->funcs)
2729 {
2730 WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2731 WindowFunc *wfunc = wfuncstate->wfunc;
2734 int i;
2735
2736 if (wfunc->winref != node->winref) /* planner screwed up? */
2737 elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2738 wfunc->winref, node->winref);
2739
2740 /*
2741 * Look for a previous duplicate window function, which needs the same
2742 * ignore_nulls value
2743 */
2744 for (i = 0; i <= wfuncno; i++)
2745 {
2746 if (equal(wfunc, perfunc[i].wfunc) &&
2747 !contain_volatile_functions((Node *) wfunc))
2748 break;
2749 }
2750 if (i <= wfuncno && wfunc->ignore_nulls == perfunc[i].ignore_nulls)
2751 {
2752 /* Found a match to an existing entry, so just mark it */
2753 wfuncstate->wfuncno = i;
2754 continue;
2755 }
2756
2757 /* Nope, so assign a new PerAgg record */
2758 perfuncstate = &perfunc[++wfuncno];
2759
2760 /* Mark WindowFunc state node with assigned index in the result array */
2761 wfuncstate->wfuncno = wfuncno;
2762
2763 /* Check permission to call window function */
2765 ACL_EXECUTE);
2766 if (aclresult != ACLCHECK_OK)
2768 get_func_name(wfunc->winfnoid));
2769 InvokeFunctionExecuteHook(wfunc->winfnoid);
2770
2771 /* Fill in the perfuncstate data */
2772 perfuncstate->wfuncstate = wfuncstate;
2773 perfuncstate->wfunc = wfunc;
2774 perfuncstate->numArguments = list_length(wfuncstate->args);
2775 perfuncstate->winCollation = wfunc->inputcollid;
2776
2777 get_typlenbyval(wfunc->wintype,
2778 &perfuncstate->resulttypeLen,
2779 &perfuncstate->resulttypeByVal);
2780
2781 /*
2782 * If it's really just a plain aggregate function, we'll emulate the
2783 * Agg environment for it.
2784 */
2785 perfuncstate->plain_agg = wfunc->winagg;
2786 if (wfunc->winagg)
2787 {
2789
2790 perfuncstate->aggno = ++aggno;
2791 peraggstate = &winstate->peragg[aggno];
2792 initialize_peragg(winstate, wfunc, peraggstate);
2793 peraggstate->wfuncno = wfuncno;
2794 }
2795 else
2796 {
2798
2799 winobj->winstate = winstate;
2800 winobj->argstates = wfuncstate->args;
2801 winobj->localmem = NULL;
2802 perfuncstate->winobj = winobj;
2803 winobj->ignore_nulls = wfunc->ignore_nulls;
2805
2806 /* It's a real window function, so set up to call it. */
2807 fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2808 econtext->ecxt_per_query_memory);
2809 fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2810 }
2811 }
2812
2813 /* Update numfuncs, numaggs to match number of unique functions found */
2814 winstate->numfuncs = wfuncno + 1;
2815 winstate->numaggs = aggno + 1;
2816
2817 /* Set up WindowObject for aggregates, if needed */
2818 if (winstate->numaggs > 0)
2819 {
2821
2822 agg_winobj->winstate = winstate;
2823 agg_winobj->argstates = NIL;
2824 agg_winobj->localmem = NULL;
2825 /* make sure markptr = -1 to invalidate. It may not get used */
2826 agg_winobj->markptr = -1;
2827 agg_winobj->readptr = -1;
2828 winstate->agg_winobj = agg_winobj;
2829 }
2830
2831 /* Set the status to running */
2832 winstate->status = WINDOWAGG_RUN;
2833
2834 /* initialize frame bound offset expressions */
2835 winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2836 (PlanState *) winstate);
2837 winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2838 (PlanState *) winstate);
2839
2840 /* Lookup in_range support functions if needed */
2841 if (OidIsValid(node->startInRangeFunc))
2842 fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2843 if (OidIsValid(node->endInRangeFunc))
2844 fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2845 winstate->inRangeColl = node->inRangeColl;
2846 winstate->inRangeAsc = node->inRangeAsc;
2847 winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2848
2849 winstate->all_first = true;
2850 winstate->partition_spooled = false;
2851 winstate->more_partitions = false;
2852 winstate->next_partition = true;
2853
2854 return winstate;
2855}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3880
bool contain_volatile_functions(Node *clause)
Definition clauses.c:551
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:250
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition execUtils.c:709
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition execUtils.c:588
@ WINDOWAGG_RUN
Definition execnodes.h:2521
#define EXEC_FLAG_BACKWARD
Definition executor.h:70
#define EXEC_FLAG_MARK
Definition executor.h:71
#define palloc0_array(type, count)
Definition fe_memutils.h:92
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition fmgr.c:139
#define fmgr_info_set_expr(expr, finfo)
Definition fmgr.h:135
char * get_func_name(Oid funcid)
Definition lsyscache.c:1839
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
Oid GetUserId(void)
Definition miscinit.c:470
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)
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:621
@ OBJECT_FUNCTION
#define FRAMEOPTION_RANGE
Definition parsenodes.h:613
#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:294
bool outeropsset
Definition execnodes.h:1286
const TupleTableSlotOps * outerops
Definition execnodes.h:1278
ExprState * qual
Definition execnodes.h:1224
bool outeropsfixed
Definition execnodes.h:1282
EState * state
Definition execnodes.h:1203
ExecProcNodeMtd ExecProcNode
Definition execnodes.h:1207
List * qual
Definition plannodes.h:237
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1662
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
FmgrInfo endInRangeFunc
Definition execnodes.h:2563
FmgrInfo startInRangeFunc
Definition execnodes.h:2562
ExprState * runcondition
Definition execnodes.h:2574
TupleTableSlot * temp_slot_2
Definition execnodes.h:2612
WindowAggStatus status
Definition execnodes.h:2553
bool inRangeNullsFirst
Definition execnodes.h:2566
ExprState * partEqfunction
Definition execnodes.h:2538
bool use_pass_through
Definition execnodes.h:2569
int partNumCols
Definition plannodes.h:1262
Oid endInRangeFunc
Definition plannodes.h:1306
Node * endOffset
Definition plannodes.h:1292
bool topWindow
Definition plannodes.h:1321
Oid inRangeColl
Definition plannodes.h:1309
Node * startOffset
Definition plannodes.h:1289
List * runCondition
Definition plannodes.h:1295
Oid startInRangeFunc
Definition plannodes.h:1303
bool inRangeAsc
Definition plannodes.h:1312
Index winref
Definition plannodes.h:1259
bool inRangeNullsFirst
Definition plannodes.h:1315
int ordNumCols
Definition plannodes.h:1274
int frameOptions
Definition plannodes.h:1286
WindowFunc * wfunc
Definition execnodes.h:959
Index winref
Definition primnodes.h:612
WindowAggState * winstate

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::agg_row_slot, WindowAggState::agg_winobj, WindowAggState::aggcontext, 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(), fb(), WindowAggState::first_part_slot, 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, WindowAggState::numfuncs, object_aclcheck(), OBJECT_FUNCTION, OidIsValid, WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::outerops, PlanState::outeropsfixed, PlanState::outeropsset, outerPlan, outerPlanState, palloc0_array, WindowAggState::partcontext, WindowAggState::partEqfunction, WindowAggState::partition_spooled, WindowAgg::partNumCols, WindowAggState::peragg, WindowAggState::perfunc, PlanState::plan, WindowAgg::plan, ScanState::ps, PlanState::ps_ExprContext, PlanState::qual, Plan::qual, WindowObjectData::readptr, 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, WindowFuncExprState::wfunc, WindowFuncExprState::wfuncno, WINDOWAGG_RUN, WindowFunc::winfnoid, WindowAgg::winref, WindowFunc::winref, and WindowObjectData::winstate.

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2897 of file nodeWindowAgg.c.

2898{
2900 ExprContext *econtext = node->ss.ps.ps_ExprContext;
2901
2902 node->status = WINDOWAGG_RUN;
2903 node->all_first = true;
2904
2905 /* release tuplestore et al */
2906 release_partition(node);
2907
2908 /* release all temp tuples, but especially first_part_slot */
2914 if (node->framehead_slot)
2916 if (node->frametail_slot)
2918
2919 /* Forget current wfunc values */
2920 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2921 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2922
2923 /*
2924 * if chgParam of subnode is not null then plan will be re-scanned by
2925 * first ExecProcNode.
2926 */
2927 if (outerPlan->chgParam == NULL)
2929}
#define MemSet(start, val, len)
Definition c.h:1107
void ExecReScan(PlanState *node)
Definition execAmi.c:78

References WindowAggState::agg_row_slot, WindowAggState::all_first, ExprContext::ecxt_aggnulls, ExprContext::ecxt_aggvalues, ExecClearTuple(), ExecReScan(), fb(), 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 2281 of file nodeWindowAgg.c.

2282{
2283 WindowAggState *winstate = castNode(WindowAggState, pstate);
2284 TupleTableSlot *slot;
2285 ExprContext *econtext;
2286 int i;
2287 int numfuncs;
2288
2290
2291 if (winstate->status == WINDOWAGG_DONE)
2292 return NULL;
2293
2294 /*
2295 * Compute frame offset values, if any, during first call (or after a
2296 * rescan). These are assumed to hold constant throughout the scan; if
2297 * user gives us a volatile expression, we'll only use its initial value.
2298 */
2299 if (unlikely(winstate->all_first))
2301
2302 /* We need to loop as the runCondition or qual may filter out tuples */
2303 for (;;)
2304 {
2305 if (winstate->next_partition)
2306 {
2307 /* Initialize for first partition and set current row = 0 */
2308 begin_partition(winstate);
2309 /* If there are no input rows, we'll detect that and exit below */
2310 }
2311 else
2312 {
2313 /* Advance current row within partition */
2314 winstate->currentpos++;
2315 /* This might mean that the frame moves, too */
2316 winstate->framehead_valid = false;
2317 winstate->frametail_valid = false;
2318 /* we don't need to invalidate grouptail here; see below */
2319 }
2320
2321 /*
2322 * Spool all tuples up to and including the current row, if we haven't
2323 * already
2324 */
2325 spool_tuples(winstate, winstate->currentpos);
2326
2327 /* Move to the next partition if we reached the end of this partition */
2328 if (winstate->partition_spooled &&
2329 winstate->currentpos >= winstate->spooled_rows)
2330 {
2331 release_partition(winstate);
2332
2333 if (winstate->more_partitions)
2334 {
2335 begin_partition(winstate);
2336 Assert(winstate->spooled_rows > 0);
2337
2338 /* Come out of pass-through mode when changing partition */
2339 winstate->status = WINDOWAGG_RUN;
2340 }
2341 else
2342 {
2343 /* No further partitions? We're done */
2344 winstate->status = WINDOWAGG_DONE;
2345 return NULL;
2346 }
2347 }
2348
2349 /* final output execution is in ps_ExprContext */
2350 econtext = winstate->ss.ps.ps_ExprContext;
2351
2352 /* Clear the per-output-tuple context for current row */
2353 ResetExprContext(econtext);
2354
2355 /*
2356 * Read the current row from the tuplestore, and save in
2357 * ScanTupleSlot. (We can't rely on the outerplan's output slot
2358 * because we may have to read beyond the current row. Also, we have
2359 * to actually copy the row out of the tuplestore, since window
2360 * function evaluation might cause the tuplestore to dump its state to
2361 * disk.)
2362 *
2363 * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2364 * we must also detect entering a new peer group and update associated
2365 * state when that happens. We use temp_slot_2 to temporarily hold
2366 * the previous row for this purpose.
2367 *
2368 * Current row must be in the tuplestore, since we spooled it above.
2369 */
2371 if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2374 winstate->currentpos > 0)
2375 {
2376 ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2377 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2378 winstate->ss.ss_ScanTupleSlot))
2379 elog(ERROR, "unexpected end of tuplestore");
2380 if (!are_peers(winstate, winstate->temp_slot_2,
2381 winstate->ss.ss_ScanTupleSlot))
2382 {
2383 winstate->currentgroup++;
2384 winstate->groupheadpos = winstate->currentpos;
2385 winstate->grouptail_valid = false;
2386 }
2387 ExecClearTuple(winstate->temp_slot_2);
2388 }
2389 else
2390 {
2391 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2392 winstate->ss.ss_ScanTupleSlot))
2393 elog(ERROR, "unexpected end of tuplestore");
2394 }
2395
2396 /* don't evaluate the window functions when we're in pass-through mode */
2397 if (winstate->status == WINDOWAGG_RUN)
2398 {
2399 /*
2400 * Evaluate true window functions
2401 */
2402 numfuncs = winstate->numfuncs;
2403 for (i = 0; i < numfuncs; i++)
2404 {
2405 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2406
2407 if (perfuncstate->plain_agg)
2408 continue;
2410 &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2411 &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2412 }
2413
2414 /*
2415 * Evaluate aggregates
2416 */
2417 if (winstate->numaggs > 0)
2418 eval_windowaggregates(winstate);
2419 }
2420
2421 /*
2422 * If we have created auxiliary read pointers for the frame or group
2423 * boundaries, force them to be kept up-to-date, because we don't know
2424 * whether the window function(s) will do anything that requires that.
2425 * Failing to advance the pointers would result in being unable to
2426 * trim data from the tuplestore, which is bad. (If we could know in
2427 * advance whether the window functions will use frame boundary info,
2428 * we could skip creating these pointers in the first place ... but
2429 * unfortunately the window function API doesn't require that.)
2430 */
2431 if (winstate->framehead_ptr >= 0)
2432 update_frameheadpos(winstate);
2433 if (winstate->frametail_ptr >= 0)
2434 update_frametailpos(winstate);
2435 if (winstate->grouptail_ptr >= 0)
2436 update_grouptailpos(winstate);
2437
2438 /*
2439 * Truncate any no-longer-needed rows from the tuplestore.
2440 */
2441 tuplestore_trim(winstate->buffer);
2442
2443 /*
2444 * Form and return a projection tuple using the windowfunc results and
2445 * the current row. Setting ecxt_outertuple arranges that any Vars
2446 * will be evaluated with respect to that row.
2447 */
2448 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2449
2450 slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2451
2452 if (winstate->status == WINDOWAGG_RUN)
2453 {
2454 econtext->ecxt_scantuple = slot;
2455
2456 /*
2457 * Now evaluate the run condition to see if we need to go into
2458 * pass-through mode, or maybe stop completely.
2459 */
2460 if (!ExecQual(winstate->runcondition, econtext))
2461 {
2462 /*
2463 * Determine which mode to move into. If there is no
2464 * PARTITION BY clause and we're the top-level WindowAgg then
2465 * we're done. This tuple and any future tuples cannot
2466 * possibly match the runcondition. However, when there is a
2467 * PARTITION BY clause or we're not the top-level window we
2468 * can't just stop as we need to either process other
2469 * partitions or ensure WindowAgg nodes above us receive all
2470 * of the tuples they need to process their WindowFuncs.
2471 */
2472 if (winstate->use_pass_through)
2473 {
2474 /*
2475 * When switching into a pass-through mode, we'd better
2476 * NULLify the aggregate results as these are no longer
2477 * updated and NULLifying them avoids the old stale
2478 * results lingering. Some of these might be byref types
2479 * so we can't have them pointing to free'd memory. The
2480 * planner insisted that quals used in the runcondition
2481 * are strict, so the top-level WindowAgg will always
2482 * filter these NULLs out in the filter clause.
2483 */
2484 numfuncs = winstate->numfuncs;
2485 for (i = 0; i < numfuncs; i++)
2486 {
2487 econtext->ecxt_aggvalues[i] = (Datum) 0;
2488 econtext->ecxt_aggnulls[i] = true;
2489 }
2490
2491 /*
2492 * STRICT pass-through mode is required for the top window
2493 * when there is a PARTITION BY clause. Otherwise we must
2494 * ensure we store tuples that don't match the
2495 * runcondition so they're available to WindowAggs above.
2496 */
2497 if (winstate->top_window)
2498 {
2500 continue;
2501 }
2502 else
2503 {
2504 winstate->status = WINDOWAGG_PASSTHROUGH;
2505 }
2506 }
2507 else
2508 {
2509 /*
2510 * Pass-through not required. We can just return NULL.
2511 * Nothing else will match the runcondition.
2512 */
2513 winstate->status = WINDOWAGG_DONE;
2514 return NULL;
2515 }
2516 }
2517
2518 /*
2519 * Filter out any tuples we don't need in the top-level WindowAgg.
2520 */
2521 if (!ExecQual(winstate->ss.ps.qual, econtext))
2522 {
2523 InstrCountFiltered1(winstate, 1);
2524 continue;
2525 }
2526
2527 break;
2528 }
2529
2530 /*
2531 * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2532 * we're anything apart from the top window.
2533 */
2534 else if (!winstate->top_window)
2535 break;
2536 }
2537
2538 return slot;
2539}
#define InstrCountFiltered1(node, delta)
Definition execnodes.h:1307
@ WINDOWAGG_PASSTHROUGH
Definition execnodes.h:2522
@ WINDOWAGG_DONE
Definition execnodes.h:2520
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition execnodes.h:2523
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition executor.h:493
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition executor.h:529
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
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:629
#define FRAMEOPTION_EXCLUDE_GROUP
Definition parsenodes.h:628
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ProjectionInfo * ps_ProjInfo
Definition execnodes.h:1243
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition tuplestore.c:508
void tuplestore_trim(Tuplestorestate *state)

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(), fb(), 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, 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, 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 629 of file nodeWindowAgg.c.

633{
635
637
638 /*
639 * Apply the agg's finalfn if one is provided, else return transValue.
640 */
641 if (OidIsValid(peraggstate->finalfn_oid))
642 {
644 int numFinalArgs = peraggstate->numFinalArgs;
645 bool anynull;
646 int i;
647
649 numFinalArgs,
650 perfuncstate->winCollation,
651 (Node *) winstate, NULL);
652 fcinfo->args[0].value =
654 peraggstate->transValueIsNull,
655 peraggstate->transtypeLen);
656 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
657 anynull = peraggstate->transValueIsNull;
658
659 /* Fill any remaining argument positions with nulls */
660 for (i = 1; i < numFinalArgs; i++)
661 {
662 fcinfo->args[i].value = (Datum) 0;
663 fcinfo->args[i].isnull = true;
664 anynull = true;
665 }
666
667 if (fcinfo->flinfo->fn_strict && anynull)
668 {
669 /* don't call a strict function with NULL inputs */
670 *result = (Datum) 0;
671 *isnull = true;
672 }
673 else
674 {
675 Datum res;
676
677 winstate->curaggcontext = peraggstate->aggcontext;
678 res = FunctionCallInvoke(fcinfo);
679 winstate->curaggcontext = NULL;
680 *isnull = fcinfo->isnull;
682 fcinfo->isnull,
683 peraggstate->resulttypeLen);
684 }
685 }
686 else
687 {
688 *result =
690 peraggstate->transValueIsNull,
691 peraggstate->transtypeLen);
692 *isnull = peraggstate->transValueIsNull;
693 }
694
696}
#define MakeExpandedObjectReadOnly(d, isnull, typlen)

References ExprContext::ecxt_per_tuple_memory, fb(), FUNC_MAX_ARGS, FunctionCallInvoke, i, InitFunctionCallInfoData, LOCAL_FCINFO, MakeExpandedObjectReadOnly, MemoryContextSwitchTo(), OidIsValid, ScanState::ps, PlanState::ps_ExprContext, result, and WindowAggState::ss.

Referenced by eval_windowaggregates().

◆ get_notnull_info()

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

Definition at line 3593 of file nodeWindowAgg.c.

3594{
3595 uint8 *mbp;
3596 uint8 mb;
3597 int64 bpos;
3598
3599 if (!winobj->notnull_info_cacheable[argno])
3600 return NN_UNKNOWN;
3601
3602 grow_notnull_info(winobj, pos, argno);
3603 bpos = NN_POS_TO_BYTES(pos);
3604 mbp = winobj->notnull_info[argno];
3605 mb = mbp[bpos];
3606 return (mb >> (NN_SHIFT(pos))) & NN_MASK;
3607}
uint8_t uint8
Definition c.h:622
static void grow_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_MASK
#define NN_SHIFT(pos)
#define NN_UNKNOWN
bool * notnull_info_cacheable

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

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3210 of file nodeWindowAgg.c.

3211{
3212 Oid typinput,
3213 typioparam;
3214 char *strInitVal;
3215 Datum initVal;
3216
3217 getTypeInputInfo(transtype, &typinput, &typioparam);
3220 typioparam, -1);
3222 return initVal;
3223}
#define TextDatumGetCString(d)
Definition builtins.h:99
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition fmgr.c:1755
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition lsyscache.c:3107
unsigned int Oid

References fb(), 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 3346 of file nodeWindowAgg.c.

3348{
3349 WindowAggState *winstate;
3350 ExprContext *econtext;
3351 TupleTableSlot *slot;
3352
3353 winstate = winobj->winstate;
3354 slot = winstate->temp_slot_1;
3355 if (!window_gettupleslot(winobj, abs_pos, slot))
3356 {
3357 /* out of partition */
3358 if (isout)
3359 *isout = true;
3360 *isnull = true;
3361 return (Datum) 0;
3362 }
3363
3364 if (isout)
3365 *isout = false;
3366 econtext = winstate->ss.ps.ps_ExprContext;
3367 econtext->ecxt_outertuple = slot;
3369 (winobj->argstates, argno),
3370 econtext, isnull);
3371}
static void * list_nth(const List *list, int n)
Definition pg_list.h:331

References WindowObjectData::argstates, ExprContext::ecxt_outertuple, ExecEvalExpr(), fb(), 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 3549 of file nodeWindowAgg.c.

3550{
3551/* initial number of notnull info members */
3552#define INIT_NOT_NULL_INFO_NUM 128
3553
3554 if (pos >= winobj->num_notnull_info[argno])
3555 {
3556 /* We may be called in a short-lived context */
3559
3560 for (;;)
3561 {
3563 (winobj->num_notnull_info[argno]);
3564 Size newsize;
3565
3566 if (oldsize == 0) /* memory has not been allocated yet for this
3567 * arg */
3568 {
3570 winobj->notnull_info[argno] = palloc0(newsize);
3571 }
3572 else
3573 {
3574 newsize = oldsize * 2;
3575 winobj->notnull_info[argno] =
3577 }
3579 if (winobj->num_notnull_info[argno] > pos)
3580 break;
3581 }
3582 MemoryContextSwitchTo(oldcontext);
3583 }
3584}
size_t Size
Definition c.h:689
void * repalloc0(void *pointer, Size oldsize, Size size)
Definition mcxt.c:1707
void * palloc0(Size size)
Definition mcxt.c:1420
#define INIT_NOT_NULL_INFO_NUM
#define NN_BYTES_TO_POS(bytes)

References ExprContext::ecxt_per_query_memory, fb(), 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 3379 of file nodeWindowAgg.c.

3382{
3383 WindowAggState *winstate;
3384 ExprContext *econtext;
3385 TupleTableSlot *slot;
3386 Datum datum;
3387 int64 abs_pos;
3389 int notnull_offset;
3390 int notnull_relpos;
3391 int forward;
3392
3393 Assert(WindowObjectIsValid(winobj));
3394 winstate = winobj->winstate;
3395 econtext = winstate->ss.ps.ps_ExprContext;
3396 slot = winstate->temp_slot_1;
3397 datum = (Datum) 0;
3398 notnull_offset = 0;
3400
3401 switch (seektype)
3402 {
3404 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3405 abs_pos = mark_pos = 0; /* keep compiler quiet */
3406 break;
3407 case WINDOW_SEEK_HEAD:
3408 /* rejecting relpos < 0 is easy and simplifies code below */
3409 if (relpos < 0)
3410 goto out_of_frame;
3411 update_frameheadpos(winstate);
3412 abs_pos = winstate->frameheadpos;
3413 mark_pos = winstate->frameheadpos;
3414 forward = 1;
3415 break;
3416 case WINDOW_SEEK_TAIL:
3417 /* rejecting relpos > 0 is easy and simplifies code below */
3418 if (relpos > 0)
3419 goto out_of_frame;
3420 update_frametailpos(winstate);
3421 abs_pos = winstate->frametailpos - 1;
3422 mark_pos = 0; /* keep compiler quiet */
3423 forward = -1;
3424 break;
3425 default:
3426 elog(ERROR, "unrecognized window seek type: %d", seektype);
3427 abs_pos = mark_pos = 0; /* keep compiler quiet */
3428 break;
3429 }
3430
3431 /*
3432 * Get the next nonnull value in the frame, moving forward or backward
3433 * until we find a value or reach the frame's end.
3434 */
3435 do
3436 {
3437 int inframe;
3438 int v;
3439
3440 /*
3441 * Check apparent out of frame case. We need to do this because we
3442 * may not call window_gettupleslot before row_is_in_frame, which
3443 * supposes abs_pos is never negative.
3444 */
3445 if (abs_pos < 0)
3446 goto out_of_frame;
3447
3448 /* check whether row is in frame */
3449 inframe = row_is_in_frame(winobj, abs_pos, slot, true);
3450 if (inframe == -1)
3451 goto out_of_frame;
3452 else if (inframe == 0)
3453 goto advance;
3454
3455 if (isout)
3456 *isout = false;
3457
3458 v = get_notnull_info(winobj, abs_pos, argno);
3459 if (v == NN_NULL) /* this row is known to be NULL */
3460 goto advance;
3461
3462 else if (v == NN_UNKNOWN) /* need to check NULL or not */
3463 {
3464 if (!window_gettupleslot(winobj, abs_pos, slot))
3465 goto out_of_frame;
3466
3467 econtext->ecxt_outertuple = slot;
3468 datum = ExecEvalExpr(
3469 (ExprState *) list_nth(winobj->argstates,
3470 argno), econtext,
3471 isnull);
3472 if (!*isnull)
3474
3475 /* record the row status */
3476 put_notnull_info(winobj, abs_pos, argno, *isnull);
3477 }
3478 else /* this row is known to be NOT NULL */
3479 {
3482 {
3483 /* to prepare exiting this loop, datum needs to be set */
3484 if (!window_gettupleslot(winobj, abs_pos, slot))
3485 goto out_of_frame;
3486
3487 econtext->ecxt_outertuple = slot;
3488 datum = ExecEvalExpr(
3490 (winobj->argstates, argno),
3491 econtext, isnull);
3492 }
3493 }
3494advance:
3495 abs_pos += forward;
3496 } while (notnull_offset <= notnull_relpos);
3497
3498 if (set_mark)
3500
3501 return datum;
3502
3504 if (isout)
3505 *isout = true;
3506 *isnull = true;
3507 return (Datum) 0;
3508}
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(), fb(), 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 3516 of file nodeWindowAgg.c.

3517{
3518 int numargs = perfuncstate->numArguments;
3519
3520 if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3521 {
3522 int argno = 0;
3523 ListCell *lc;
3524
3525 winobj->notnull_info = palloc0_array(uint8 *, numargs);
3526 winobj->num_notnull_info = palloc0_array(int64, numargs);
3527 winobj->notnull_info_cacheable = palloc_array(bool, numargs);
3528
3529 foreach(lc, perfuncstate->wfunc->args)
3530 {
3531 Node *arg = (Node *) lfirst(lc);
3532
3533 winobj->notnull_info_cacheable[argno] =
3536
3537 argno++;
3538 }
3539 }
3540}
bool contain_subplans(Node *clause)
Definition clauses.c:343
#define palloc_array(type, count)
Definition fe_memutils.h:91

References arg, contain_subplans(), contain_volatile_functions(), fb(), WindowObjectData::ignore_nulls, lfirst, WindowObjectData::notnull_info, WindowObjectData::notnull_info_cacheable, WindowObjectData::num_notnull_info, palloc0_array, palloc_array, and PARSER_IGNORE_NULLS.

Referenced by ExecInitWindowAgg().

◆ initialize_peragg()

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

Definition at line 2937 of file nodeWindowAgg.c.

2939{
2941 int numArguments;
2944 Oid aggtranstype;
2947 bool use_ma_code;
2948 Oid transfn_oid,
2949 invtransfn_oid,
2950 finalfn_oid;
2951 bool finalextra;
2952 char finalmodify;
2955 *finalfnexpr;
2957 int i;
2958 ListCell *lc;
2959
2960 numArguments = list_length(wfunc->args);
2961
2962 i = 0;
2963 foreach(lc, wfunc->args)
2964 {
2965 inputTypes[i++] = exprType((Node *) lfirst(lc));
2966 }
2967
2970 elog(ERROR, "cache lookup failed for aggregate %u",
2971 wfunc->winfnoid);
2973
2974 /*
2975 * Figure out whether we want to use the moving-aggregate implementation,
2976 * and collect the right set of fields from the pg_aggregate entry.
2977 *
2978 * It's possible that an aggregate would supply a safe moving-aggregate
2979 * implementation and an unsafe normal one, in which case our hand is
2980 * forced. Otherwise, if the frame head can't move, we don't need
2981 * moving-aggregate code. Even if we'd like to use it, don't do so if the
2982 * aggregate's arguments (and FILTER clause if any) contain any calls to
2983 * volatile functions. Otherwise, the difference between restarting and
2984 * not restarting the aggregation would be user-visible.
2985 *
2986 * We also don't risk using moving aggregates when there are subplans in
2987 * the arguments or FILTER clause. This is partly because
2988 * contain_volatile_functions() doesn't look inside subplans; but there
2989 * are other reasons why a subplan's output might be volatile. For
2990 * example, syncscan mode can render the results nonrepeatable.
2991 */
2992 if (!OidIsValid(aggform->aggminvtransfn))
2993 use_ma_code = false; /* sine qua non */
2994 else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2995 aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2996 use_ma_code = true; /* decision forced by safety */
2998 use_ma_code = false; /* non-moving frame head */
2999 else if (contain_volatile_functions((Node *) wfunc))
3000 use_ma_code = false; /* avoid possible behavioral change */
3001 else if (contain_subplans((Node *) wfunc))
3002 use_ma_code = false; /* subplans might contain volatile functions */
3003 else
3004 use_ma_code = true; /* yes, let's use it */
3005 if (use_ma_code)
3006 {
3007 peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
3008 peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
3009 peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
3010 finalextra = aggform->aggmfinalextra;
3011 finalmodify = aggform->aggmfinalmodify;
3012 aggtranstype = aggform->aggmtranstype;
3014 }
3015 else
3016 {
3017 peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
3018 peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
3019 peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
3020 finalextra = aggform->aggfinalextra;
3021 finalmodify = aggform->aggfinalmodify;
3022 aggtranstype = aggform->aggtranstype;
3024 }
3025
3026 /*
3027 * ExecInitWindowAgg already checked permission to call aggregate function
3028 * ... but we still need to check the component functions
3029 */
3030
3031 /* Check that aggregate owner has permission to call component fns */
3032 {
3034 Oid aggOwner;
3035
3037 ObjectIdGetDatum(wfunc->winfnoid));
3039 elog(ERROR, "cache lookup failed for function %u",
3040 wfunc->winfnoid);
3043
3045 ACL_EXECUTE);
3046 if (aclresult != ACLCHECK_OK)
3048 get_func_name(transfn_oid));
3049 InvokeFunctionExecuteHook(transfn_oid);
3050
3051 if (OidIsValid(invtransfn_oid))
3052 {
3054 ACL_EXECUTE);
3055 if (aclresult != ACLCHECK_OK)
3057 get_func_name(invtransfn_oid));
3058 InvokeFunctionExecuteHook(invtransfn_oid);
3059 }
3060
3061 if (OidIsValid(finalfn_oid))
3062 {
3064 ACL_EXECUTE);
3065 if (aclresult != ACLCHECK_OK)
3067 get_func_name(finalfn_oid));
3068 InvokeFunctionExecuteHook(finalfn_oid);
3069 }
3070 }
3071
3072 /*
3073 * If the selected finalfn isn't read-only, we can't run this aggregate as
3074 * a window function. This is a user-facing error, so we take a bit more
3075 * care with the error message than elsewhere in this function.
3076 */
3078 ereport(ERROR,
3080 errmsg("aggregate function %s does not support use as a window function",
3081 format_procedure(wfunc->winfnoid))));
3082
3083 /* Detect how many arguments to pass to the finalfn */
3084 if (finalextra)
3085 peraggstate->numFinalArgs = numArguments + 1;
3086 else
3087 peraggstate->numFinalArgs = 1;
3088
3089 /* resolve actual type of transition state, if polymorphic */
3090 aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
3091 aggtranstype,
3092 inputTypes,
3093 numArguments);
3094
3095 /* build expression trees using actual argument & result types */
3097 numArguments,
3098 0, /* no ordered-set window functions yet */
3099 false, /* no variadic window functions yet */
3100 aggtranstype,
3101 wfunc->inputcollid,
3102 transfn_oid,
3103 invtransfn_oid,
3104 &transfnexpr,
3106
3107 /* set up infrastructure for calling the transfn(s) and finalfn */
3108 fmgr_info(transfn_oid, &peraggstate->transfn);
3110
3111 if (OidIsValid(invtransfn_oid))
3112 {
3113 fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
3115 }
3116
3117 if (OidIsValid(finalfn_oid))
3118 {
3120 peraggstate->numFinalArgs,
3121 aggtranstype,
3122 wfunc->wintype,
3123 wfunc->inputcollid,
3124 finalfn_oid,
3125 &finalfnexpr);
3126 fmgr_info(finalfn_oid, &peraggstate->finalfn);
3128 }
3129
3130 /* get info about relevant datatypes */
3131 get_typlenbyval(wfunc->wintype,
3132 &peraggstate->resulttypeLen,
3133 &peraggstate->resulttypeByVal);
3134 get_typlenbyval(aggtranstype,
3135 &peraggstate->transtypeLen,
3136 &peraggstate->transtypeByVal);
3137
3138 /*
3139 * initval is potentially null, so don't try to access it as a struct
3140 * field. Must do it the hard way with SysCacheGetAttr.
3141 */
3143 &peraggstate->initValueIsNull);
3144
3145 if (peraggstate->initValueIsNull)
3146 peraggstate->initValue = (Datum) 0;
3147 else
3149 aggtranstype);
3150
3151 /*
3152 * If the transfn is strict and the initval is NULL, make sure input type
3153 * and transtype are the same (or at least binary-compatible), so that
3154 * it's OK to use the first input value as the initial transValue. This
3155 * should have been checked at agg definition time, but we must check
3156 * again in case the transfn's strictness property has been changed.
3157 */
3158 if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
3159 {
3160 if (numArguments < 1 ||
3161 !IsBinaryCoercible(inputTypes[0], aggtranstype))
3162 ereport(ERROR,
3164 errmsg("aggregate %u needs to have compatible input type and transition type",
3165 wfunc->winfnoid)));
3166 }
3167
3168 /*
3169 * Insist that forward and inverse transition functions have the same
3170 * strictness setting. Allowing them to differ would require handling
3171 * more special cases in advance_windowaggregate and
3172 * advance_windowaggregate_base, for no discernible benefit. This should
3173 * have been checked at agg definition time, but we must check again in
3174 * case either function's strictness property has been changed.
3175 */
3176 if (OidIsValid(invtransfn_oid) &&
3177 peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
3178 ereport(ERROR,
3180 errmsg("strictness of aggregate's forward and inverse transition functions must match")));
3181
3182 /*
3183 * Moving aggregates use their own aggcontext.
3184 *
3185 * This is necessary because they might restart at different times, so we
3186 * might never be able to reset the shared context otherwise. We can't
3187 * make it the aggregates' responsibility to clean up after themselves,
3188 * because strict aggregates must be restarted whenever we remove their
3189 * last non-NULL input, which the aggregate won't be aware is happening.
3190 * Also, just pfree()ing the transValue upon restarting wouldn't help,
3191 * since we'd miss any indirectly referenced data. We could, in theory,
3192 * make the memory allocation rules for moving aggregates different than
3193 * they have historically been for plain aggregates, but that seems grotty
3194 * and likely to lead to memory leaks.
3195 */
3196 if (OidIsValid(invtransfn_oid))
3197 peraggstate->aggcontext =
3199 "WindowAgg Per Aggregate",
3201 else
3202 peraggstate->aggcontext = winstate->aggcontext;
3203
3205
3206 return peraggstate;
3207}
int16 AttrNumber
Definition attnum.h:21
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
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:2359
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition parse_agg.c:2148
void build_aggregate_transfn_expr(Oid *agg_input_types, int agg_num_inputs, int agg_num_direct_inputs, bool agg_variadic, Oid agg_state_type, Oid agg_input_collation, Oid transfn_oid, Oid invtransfn_oid, Expr **transfnexpr, Expr **invtransfnexpr)
Definition parse_agg.c:2251
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition parsenodes.h:617
END_CATALOG_STRUCT typedef FormData_pg_aggregate * Form_pg_aggregate
END_CATALOG_STRUCT typedef FormData_pg_proc * Form_pg_proc
Definition pg_proc.h:140
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
#define InvalidOid
char * format_procedure(Oid procedure_oid)
Definition regproc.c:305
List * args
Definition primnodes.h:606
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, 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(), fb(), fmgr_info(), fmgr_info_set_expr, Form_pg_aggregate, Form_pg_proc, format_procedure(), FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, FUNC_MAX_ARGS, get_func_name(), get_typlenbyval(), GetAggInitVal(), GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, InvokeFunctionExecuteHook, IsBinaryCoercible(), lfirst, list_length(), object_aclcheck(), OBJECT_FUNCTION, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), resolve_aggregate_transtype(), SearchSysCache1(), SysCacheGetAttr(), and WindowFunc::winfnoid.

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 254 of file nodeWindowAgg.c.

257{
259
260 /*
261 * If we're using a private aggcontext, we may reset it here. But if the
262 * context is shared, we don't know which other aggregates may still need
263 * it, so we must leave it to the caller to reset at an appropriate time.
264 */
265 if (peraggstate->aggcontext != winstate->aggcontext)
266 MemoryContextReset(peraggstate->aggcontext);
267
268 if (peraggstate->initValueIsNull)
269 peraggstate->transValue = peraggstate->initValue;
270 else
271 {
273 peraggstate->transValue = datumCopy(peraggstate->initValue,
274 peraggstate->transtypeByVal,
275 peraggstate->transtypeLen);
277 }
278 peraggstate->transValueIsNull = peraggstate->initValueIsNull;
279 peraggstate->transValueCount = 0;
280 peraggstate->resultValue = (Datum) 0;
281 peraggstate->resultValueIsNull = true;
282}

References WindowAggState::aggcontext, datumCopy(), fb(), MemoryContextReset(), and MemoryContextSwitchTo().

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ prepare_tuplestore()

static pg_noinline void prepare_tuplestore ( WindowAggState winstate)
static

Definition at line 1133 of file nodeWindowAgg.c.

1134{
1135 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1136 int frameOptions = winstate->frameOptions;
1137 int numfuncs = winstate->numfuncs;
1138
1139 /* we shouldn't be called if this was done already */
1140 Assert(winstate->buffer == NULL);
1141
1142 /* Create new tuplestore */
1143 winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1144
1145 /*
1146 * Set up read pointers for the tuplestore. The current pointer doesn't
1147 * need BACKWARD capability, but the per-window-function read pointers do,
1148 * and the aggregate pointer does if we might need to restart aggregation.
1149 */
1150 winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1151
1152 /* reset default REWIND capability bit for current ptr */
1153 tuplestore_set_eflags(winstate->buffer, 0);
1154
1155 /* create read pointers for aggregates, if needed */
1156 if (winstate->numaggs > 0)
1157 {
1158 WindowObject agg_winobj = winstate->agg_winobj;
1159 int readptr_flags = 0;
1160
1161 /*
1162 * If the frame head is potentially movable, or we have an EXCLUSION
1163 * clause, we might need to restart aggregation ...
1164 */
1165 if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1166 (frameOptions & FRAMEOPTION_EXCLUSION))
1167 {
1168 /* ... so create a mark pointer to track the frame head */
1169 agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1170 /* and the read pointer will need BACKWARD capability */
1172 }
1173
1174 agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1176 }
1177
1178 /* create mark and read pointers for each real window function */
1179 for (int i = 0; i < numfuncs; i++)
1180 {
1181 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1182
1183 if (!perfuncstate->plain_agg)
1184 {
1185 WindowObject winobj = perfuncstate->winobj;
1186
1187 winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1188 0);
1189 winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1191 }
1192 }
1193
1194 /*
1195 * If we are in RANGE or GROUPS mode, then determining frame boundaries
1196 * requires physical access to the frame endpoint rows, except in certain
1197 * degenerate cases. We create read pointers to point to those rows, to
1198 * simplify access and ensure that the tuplestore doesn't discard the
1199 * endpoint rows prematurely. (Must create pointers in exactly the same
1200 * cases that update_frameheadpos and update_frametailpos need them.)
1201 */
1202 winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1203
1204 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1205 {
1206 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1207 node->ordNumCols != 0) ||
1208 (frameOptions & FRAMEOPTION_START_OFFSET))
1209 winstate->framehead_ptr =
1211 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1212 node->ordNumCols != 0) ||
1213 (frameOptions & FRAMEOPTION_END_OFFSET))
1214 winstate->frametail_ptr =
1216 }
1217
1218 /*
1219 * If we have an exclusion clause that requires knowing the boundaries of
1220 * the current row's peer group, we create a read pointer to track the
1221 * tail position of the peer group (i.e., first row of the next peer
1222 * group). The head position does not require its own pointer because we
1223 * maintain that as a side effect of advancing the current row.
1224 */
1225 winstate->grouptail_ptr = -1;
1226
1227 if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1229 node->ordNumCols != 0)
1230 {
1231 winstate->grouptail_ptr =
1233 }
1234}
int work_mem
Definition globals.c:133
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition tuplestore.c:396
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition tuplestore.c:372

References WindowAggState::agg_winobj, Assert, WindowAggState::buffer, WindowAggState::current_ptr, EXEC_FLAG_BACKWARD, fb(), 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, PlanState::plan, ScanState::ps, WindowObjectData::readptr, WindowAggState::ss, tuplestore_alloc_read_pointer(), tuplestore_begin_heap(), tuplestore_set_eflags(), 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 3617 of file nodeWindowAgg.c.

3618{
3619 uint8 *mbp;
3620 uint8 mb;
3621 int64 bpos;
3622 uint8 val = isnull ? NN_NULL : NN_NOTNULL;
3623 int shift;
3624
3625 if (!winobj->notnull_info_cacheable[argno])
3626 return;
3627
3628 grow_notnull_info(winobj, pos, argno);
3629 bpos = NN_POS_TO_BYTES(pos);
3630 mbp = winobj->notnull_info[argno];
3631 mb = mbp[bpos];
3632 shift = NN_SHIFT(pos);
3633 mb &= ~(NN_MASK << shift); /* clear map */
3634 mb |= (val << shift); /* update map */
3635 mbp[bpos] = mb;
3636}
long val
Definition informix.c:689
#define NN_NOTNULL

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

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1440 of file nodeWindowAgg.c.

1441{
1442 int i;
1443
1444 for (i = 0; i < winstate->numfuncs; i++)
1445 {
1446 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1447
1448 /* Release any partition-local state of this window function */
1449 if (perfuncstate->winobj)
1451 }
1452
1453 /*
1454 * Release all partition-local memory (in particular, any partition-local
1455 * state that we might have trashed our pointers to in the above loop, and
1456 * any aggregate temp data). We don't rely on retail pfree because some
1457 * aggregates might have allocated data we don't have direct pointers to.
1458 */
1460 MemoryContextReset(winstate->aggcontext);
1461 for (i = 0; i < winstate->numaggs; i++)
1462 {
1463 if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1465 }
1466
1467 if (winstate->buffer)
1468 tuplestore_clear(winstate->buffer);
1469 winstate->partition_spooled = false;
1470 winstate->next_partition = true;
1471}
void tuplestore_clear(Tuplestorestate *state)
Definition tuplestore.c:431

References WindowStatePerAggData::aggcontext, WindowAggState::aggcontext, WindowAggState::buffer, fb(), 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 1490 of file nodeWindowAgg.c.

1492{
1493 WindowAggState *winstate = winobj->winstate;
1494 int frameOptions = winstate->frameOptions;
1495
1496 Assert(pos >= 0); /* else caller error */
1497
1498 /*
1499 * First, check frame starting conditions. We might as well delegate this
1500 * to update_frameheadpos always; it doesn't add any notable cost.
1501 */
1502 update_frameheadpos(winstate);
1503 if (pos < winstate->frameheadpos)
1504 return 0;
1505
1506 /*
1507 * Okay so far, now check frame ending conditions. Here, we avoid calling
1508 * update_frametailpos in simple cases, so as not to spool tuples further
1509 * ahead than necessary.
1510 */
1511 if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1512 {
1513 if (frameOptions & FRAMEOPTION_ROWS)
1514 {
1515 /* rows after current row are out of frame */
1516 if (pos > winstate->currentpos)
1517 return -1;
1518 }
1519 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1520 {
1521 /* following row that is not peer is out of frame */
1522 if (pos > winstate->currentpos)
1523 {
1524 if (fetch_tuple) /* need to fetch tuple? */
1525 if (!window_gettupleslot(winobj, pos, slot))
1526 return -1;
1527 if (!are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1528 return -1;
1529 }
1530 }
1531 else
1532 Assert(false);
1533 }
1534 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1535 {
1536 if (frameOptions & FRAMEOPTION_ROWS)
1537 {
1538 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1539 int64 frameendpos = 0;
1540
1541 /* rows after current row + offset are out of frame */
1542 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1543 offset = -offset;
1544
1545 /*
1546 * If we have an overflow, it means the frame end is beyond the
1547 * range of int64. Since currentpos >= 0, this can only be a
1548 * positive overflow. We treat this as meaning that the frame
1549 * extends to end of partition.
1550 */
1551 if (!pg_add_s64_overflow(winstate->currentpos, offset,
1552 &frameendpos) &&
1553 pos > frameendpos)
1554 return -1;
1555 }
1556 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1557 {
1558 /* hard cases, so delegate to update_frametailpos */
1559 update_frametailpos(winstate);
1560 if (pos >= winstate->frametailpos)
1561 return -1;
1562 }
1563 else
1564 Assert(false);
1565 }
1566
1567 /* Check exclusion clause */
1568 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1569 {
1570 if (pos == winstate->currentpos)
1571 return 0;
1572 }
1573 else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1574 ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1575 pos != winstate->currentpos))
1576 {
1577 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1578
1579 /* If no ORDER BY, all rows are peers with each other */
1580 if (node->ordNumCols == 0)
1581 return 0;
1582 /* Otherwise, check the group boundaries */
1583 if (pos >= winstate->groupheadpos)
1584 {
1585 update_grouptailpos(winstate);
1586 if (pos < winstate->grouptailpos)
1587 return 0;
1588 }
1589 }
1590
1591 /* If we get here, it's in frame */
1592 return 1;
1593}
static bool pg_add_s64_overflow(int64 a, int64 b, int64 *result)
Definition int.h:235
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition parsenodes.h:627
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition parsenodes.h:624

References are_peers(), Assert, WindowAggState::currentpos, DatumGetInt64(), WindowAggState::endOffsetValue, fb(), 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, WindowAgg::ordNumCols, pg_add_s64_overflow(), 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 1346 of file nodeWindowAgg.c.

1347{
1348 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1351 MemoryContext oldcontext;
1352
1353 if (!winstate->buffer)
1354 return; /* just a safety check */
1355 if (winstate->partition_spooled)
1356 return; /* whole partition done already */
1357
1358 /*
1359 * When in pass-through mode we can just exhaust all tuples in the current
1360 * partition. We don't need these tuples for any further window function
1361 * evaluation, however, we do need to keep them around if we're not the
1362 * top-level window as another WindowAgg node above must see these.
1363 */
1364 if (winstate->status != WINDOWAGG_RUN)
1365 {
1366 Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1368
1369 pos = -1;
1370 }
1371
1372 /*
1373 * If the tuplestore has spilled to disk, alternate reading and writing
1374 * becomes quite expensive due to frequent buffer flushes. It's cheaper
1375 * to force the entire partition to get spooled in one go.
1376 *
1377 * XXX this is a horrid kluge --- it'd be better to fix the performance
1378 * problem inside tuplestore. FIXME
1379 */
1380 else if (!tuplestore_in_memory(winstate->buffer))
1381 pos = -1;
1382
1383 outerPlan = outerPlanState(winstate);
1384
1385 /* Must be in query context to call outerplan */
1387
1388 while (winstate->spooled_rows <= pos || pos == -1)
1389 {
1391 if (TupIsNull(outerslot))
1392 {
1393 /* reached the end of the last partition */
1394 winstate->partition_spooled = true;
1395 winstate->more_partitions = false;
1396 break;
1397 }
1398
1399 if (node->partNumCols > 0)
1400 {
1401 ExprContext *econtext = winstate->tmpcontext;
1402
1403 econtext->ecxt_innertuple = winstate->first_part_slot;
1404 econtext->ecxt_outertuple = outerslot;
1405
1406 /* Check if this tuple still belongs to the current partition */
1407 if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1408 {
1409 /*
1410 * end of partition; copy the tuple for the next cycle.
1411 */
1413 winstate->partition_spooled = true;
1414 winstate->more_partitions = true;
1415 break;
1416 }
1417 }
1418
1419 /*
1420 * Remember the tuple unless we're the top-level window and we're in
1421 * pass-through mode.
1422 */
1423 if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1424 {
1425 /* Still in partition, so save it into the tuplestore */
1427 winstate->spooled_rows++;
1428 }
1429 }
1430
1431 MemoryContextSwitchTo(oldcontext);
1432}
bool tuplestore_in_memory(Tuplestorestate *state)

References Assert, WindowAggState::buffer, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExprContext::ecxt_per_query_memory, ExecCopySlot(), ExecProcNode(), ExecQualAndReset(), fb(), WindowAggState::first_part_slot, 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 1606 of file nodeWindowAgg.c.

1607{
1608 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1609 int frameOptions = winstate->frameOptions;
1610 MemoryContext oldcontext;
1611
1612 if (winstate->framehead_valid)
1613 return; /* already known for current row */
1614
1615 /* We may be called in a short-lived context */
1617
1618 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1619 {
1620 /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1621 winstate->frameheadpos = 0;
1622 winstate->framehead_valid = true;
1623 }
1624 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1625 {
1626 if (frameOptions & FRAMEOPTION_ROWS)
1627 {
1628 /* In ROWS mode, frame head is the same as current */
1629 winstate->frameheadpos = winstate->currentpos;
1630 winstate->framehead_valid = true;
1631 }
1632 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1633 {
1634 /* If no ORDER BY, all rows are peers with each other */
1635 if (node->ordNumCols == 0)
1636 {
1637 winstate->frameheadpos = 0;
1638 winstate->framehead_valid = true;
1639 MemoryContextSwitchTo(oldcontext);
1640 return;
1641 }
1642
1643 /*
1644 * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1645 * first row that is a peer of current row. We keep a copy of the
1646 * last-known frame head row in framehead_slot, and advance as
1647 * necessary. Note that if we reach end of partition, we will
1648 * leave frameheadpos = end+1 and framehead_slot empty.
1649 */
1651 winstate->framehead_ptr);
1652 if (winstate->frameheadpos == 0 &&
1653 TupIsNull(winstate->framehead_slot))
1654 {
1655 /* fetch first row into framehead_slot, if we didn't already */
1656 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1657 winstate->framehead_slot))
1658 elog(ERROR, "unexpected end of tuplestore");
1659 }
1660
1661 while (!TupIsNull(winstate->framehead_slot))
1662 {
1663 if (are_peers(winstate, winstate->framehead_slot,
1664 winstate->ss.ss_ScanTupleSlot))
1665 break; /* this row is the correct frame head */
1666 /* Note we advance frameheadpos even if the fetch fails */
1667 winstate->frameheadpos++;
1668 spool_tuples(winstate, winstate->frameheadpos);
1669 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1670 winstate->framehead_slot))
1671 break; /* end of partition */
1672 }
1673 winstate->framehead_valid = true;
1674 }
1675 else
1676 Assert(false);
1677 }
1678 else if (frameOptions & FRAMEOPTION_START_OFFSET)
1679 {
1680 if (frameOptions & FRAMEOPTION_ROWS)
1681 {
1682 /* In ROWS mode, bound is physically n before/after current */
1683 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1684
1685 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1686 offset = -offset;
1687
1688 /*
1689 * If we have an overflow, it means the frame head is beyond the
1690 * range of int64. Since currentpos >= 0, this can only be a
1691 * positive overflow. We treat this as being beyond end of
1692 * partition.
1693 */
1694 if (pg_add_s64_overflow(winstate->currentpos, offset,
1695 &winstate->frameheadpos))
1696 winstate->frameheadpos = PG_INT64_MAX;
1697
1698 /* frame head can't go before first row */
1699 if (winstate->frameheadpos < 0)
1700 winstate->frameheadpos = 0;
1701 else if (winstate->frameheadpos > winstate->currentpos + 1)
1702 {
1703 /* make sure frameheadpos is not past end of partition */
1704 spool_tuples(winstate, winstate->frameheadpos - 1);
1705 if (winstate->frameheadpos > winstate->spooled_rows)
1706 winstate->frameheadpos = winstate->spooled_rows;
1707 }
1708 winstate->framehead_valid = true;
1709 }
1710 else if (frameOptions & FRAMEOPTION_RANGE)
1711 {
1712 /*
1713 * In RANGE START_OFFSET mode, frame head is the first row that
1714 * satisfies the in_range constraint relative to the current row.
1715 * We keep a copy of the last-known frame head row in
1716 * framehead_slot, and advance as necessary. Note that if we
1717 * reach end of partition, we will leave frameheadpos = end+1 and
1718 * framehead_slot empty.
1719 */
1720 int sortCol = node->ordColIdx[0];
1721 bool sub,
1722 less;
1723
1724 /* We must have an ordering column */
1725 Assert(node->ordNumCols == 1);
1726
1727 /* Precompute flags for in_range checks */
1728 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1729 sub = true; /* subtract startOffset from current row */
1730 else
1731 sub = false; /* add it */
1732 less = false; /* normally, we want frame head >= sum */
1733 /* If sort order is descending, flip both flags */
1734 if (!winstate->inRangeAsc)
1735 {
1736 sub = !sub;
1737 less = true;
1738 }
1739
1741 winstate->framehead_ptr);
1742 if (winstate->frameheadpos == 0 &&
1743 TupIsNull(winstate->framehead_slot))
1744 {
1745 /* fetch first row into framehead_slot, if we didn't already */
1746 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1747 winstate->framehead_slot))
1748 elog(ERROR, "unexpected end of tuplestore");
1749 }
1750
1751 while (!TupIsNull(winstate->framehead_slot))
1752 {
1753 Datum headval,
1754 currval;
1755 bool headisnull,
1756 currisnull;
1757
1759 &headisnull);
1761 &currisnull);
1762 if (headisnull || currisnull)
1763 {
1764 /* order of the rows depends only on nulls_first */
1765 if (winstate->inRangeNullsFirst)
1766 {
1767 /* advance head if head is null and curr is not */
1768 if (!headisnull || currisnull)
1769 break;
1770 }
1771 else
1772 {
1773 /* advance head if head is not null and curr is null */
1774 if (headisnull || !currisnull)
1775 break;
1776 }
1777 }
1778 else
1779 {
1781 winstate->inRangeColl,
1782 headval,
1783 currval,
1784 winstate->startOffsetValue,
1785 BoolGetDatum(sub),
1786 BoolGetDatum(less))))
1787 break; /* this row is the correct frame head */
1788 }
1789 /* Note we advance frameheadpos even if the fetch fails */
1790 winstate->frameheadpos++;
1791 spool_tuples(winstate, winstate->frameheadpos);
1792 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1793 winstate->framehead_slot))
1794 break; /* end of partition */
1795 }
1796 winstate->framehead_valid = true;
1797 }
1798 else if (frameOptions & FRAMEOPTION_GROUPS)
1799 {
1800 /*
1801 * In GROUPS START_OFFSET mode, frame head is the first row of the
1802 * first peer group whose number satisfies the offset constraint.
1803 * We keep a copy of the last-known frame head row in
1804 * framehead_slot, and advance as necessary. Note that if we
1805 * reach end of partition, we will leave frameheadpos = end+1 and
1806 * framehead_slot empty.
1807 */
1808 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1809 int64 minheadgroup = 0;
1810
1811 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1812 minheadgroup = winstate->currentgroup - offset;
1813 else
1814 {
1815 /*
1816 * If we have an overflow, it means the target group is beyond
1817 * the range of int64. We treat this as "infinity", which
1818 * ensures the loop below advances to end of partition.
1819 */
1820 if (pg_add_s64_overflow(winstate->currentgroup, offset,
1821 &minheadgroup))
1823 }
1824
1826 winstate->framehead_ptr);
1827 if (winstate->frameheadpos == 0 &&
1828 TupIsNull(winstate->framehead_slot))
1829 {
1830 /* fetch first row into framehead_slot, if we didn't already */
1831 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1832 winstate->framehead_slot))
1833 elog(ERROR, "unexpected end of tuplestore");
1834 }
1835
1836 while (!TupIsNull(winstate->framehead_slot))
1837 {
1838 if (winstate->frameheadgroup >= minheadgroup)
1839 break; /* this row is the correct frame head */
1840 ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1841 /* Note we advance frameheadpos even if the fetch fails */
1842 winstate->frameheadpos++;
1843 spool_tuples(winstate, winstate->frameheadpos);
1844 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1845 winstate->framehead_slot))
1846 break; /* end of partition */
1847 if (!are_peers(winstate, winstate->temp_slot_2,
1848 winstate->framehead_slot))
1849 winstate->frameheadgroup++;
1850 }
1851 ExecClearTuple(winstate->temp_slot_2);
1852 winstate->framehead_valid = true;
1853 }
1854 else
1855 Assert(false);
1856 }
1857 else
1858 Assert(false);
1859
1860 MemoryContextSwitchTo(oldcontext);
1861}
#define PG_INT64_MAX
Definition c.h:676
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition fmgr.c:1225
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition parsenodes.h:623
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition tuptable.h:417

References are_peers(), Assert, BoolGetDatum(), WindowAggState::buffer, WindowAggState::currentgroup, WindowAggState::currentpos, DatumGetBool(), DatumGetInt64(), ExprContext::ecxt_per_query_memory, elog, ERROR, ExecClearTuple(), ExecCopySlot(), fb(), 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(), WindowAggState::inRangeAsc, WindowAggState::inRangeColl, WindowAggState::inRangeNullsFirst, MemoryContextSwitchTo(), WindowAgg::ordNumCols, pg_add_s64_overflow(), PG_INT64_MAX, 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 1874 of file nodeWindowAgg.c.

1875{
1876 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1877 int frameOptions = winstate->frameOptions;
1878 MemoryContext oldcontext;
1879
1880 if (winstate->frametail_valid)
1881 return; /* already known for current row */
1882
1883 /* We may be called in a short-lived context */
1885
1886 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1887 {
1888 /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1889 spool_tuples(winstate, -1);
1890 winstate->frametailpos = winstate->spooled_rows;
1891 winstate->frametail_valid = true;
1892 }
1893 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1894 {
1895 if (frameOptions & FRAMEOPTION_ROWS)
1896 {
1897 /* In ROWS mode, exactly the rows up to current are in frame */
1898 winstate->frametailpos = winstate->currentpos + 1;
1899 winstate->frametail_valid = true;
1900 }
1901 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1902 {
1903 /* If no ORDER BY, all rows are peers with each other */
1904 if (node->ordNumCols == 0)
1905 {
1906 spool_tuples(winstate, -1);
1907 winstate->frametailpos = winstate->spooled_rows;
1908 winstate->frametail_valid = true;
1909 MemoryContextSwitchTo(oldcontext);
1910 return;
1911 }
1912
1913 /*
1914 * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1915 * row that is a peer of current row, frame tail is the row after
1916 * that (if any). We keep a copy of the last-known frame tail row
1917 * in frametail_slot, and advance as necessary. Note that if we
1918 * reach end of partition, we will leave frametailpos = end+1 and
1919 * frametail_slot empty.
1920 */
1922 winstate->frametail_ptr);
1923 if (winstate->frametailpos == 0 &&
1924 TupIsNull(winstate->frametail_slot))
1925 {
1926 /* fetch first row into frametail_slot, if we didn't already */
1927 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1928 winstate->frametail_slot))
1929 elog(ERROR, "unexpected end of tuplestore");
1930 }
1931
1932 while (!TupIsNull(winstate->frametail_slot))
1933 {
1934 if (winstate->frametailpos > winstate->currentpos &&
1935 !are_peers(winstate, winstate->frametail_slot,
1936 winstate->ss.ss_ScanTupleSlot))
1937 break; /* this row is the frame tail */
1938 /* Note we advance frametailpos even if the fetch fails */
1939 winstate->frametailpos++;
1940 spool_tuples(winstate, winstate->frametailpos);
1941 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1942 winstate->frametail_slot))
1943 break; /* end of partition */
1944 }
1945 winstate->frametail_valid = true;
1946 }
1947 else
1948 Assert(false);
1949 }
1950 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1951 {
1952 if (frameOptions & FRAMEOPTION_ROWS)
1953 {
1954 /* In ROWS mode, bound is physically n before/after current */
1955 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1956
1957 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1958 offset = -offset;
1959
1960 /*
1961 * If we have an overflow, it means the frame tail is beyond the
1962 * range of int64. Since currentpos >= 0, this can only be a
1963 * positive overflow. We treat this as being beyond end of
1964 * partition.
1965 */
1966 if (pg_add_s64_overflow(winstate->currentpos, offset,
1967 &winstate->frametailpos) ||
1968 pg_add_s64_overflow(winstate->frametailpos, 1,
1969 &winstate->frametailpos))
1970 winstate->frametailpos = PG_INT64_MAX;
1971
1972 /* smallest allowable value of frametailpos is 0 */
1973 if (winstate->frametailpos < 0)
1974 winstate->frametailpos = 0;
1975 else if (winstate->frametailpos > winstate->currentpos + 1)
1976 {
1977 /* make sure frametailpos is not past end of partition */
1978 spool_tuples(winstate, winstate->frametailpos - 1);
1979 if (winstate->frametailpos > winstate->spooled_rows)
1980 winstate->frametailpos = winstate->spooled_rows;
1981 }
1982 winstate->frametail_valid = true;
1983 }
1984 else if (frameOptions & FRAMEOPTION_RANGE)
1985 {
1986 /*
1987 * In RANGE END_OFFSET mode, frame end is the last row that
1988 * satisfies the in_range constraint relative to the current row,
1989 * frame tail is the row after that (if any). We keep a copy of
1990 * the last-known frame tail row in frametail_slot, and advance as
1991 * necessary. Note that if we reach end of partition, we will
1992 * leave frametailpos = end+1 and frametail_slot empty.
1993 */
1994 int sortCol = node->ordColIdx[0];
1995 bool sub,
1996 less;
1997
1998 /* We must have an ordering column */
1999 Assert(node->ordNumCols == 1);
2000
2001 /* Precompute flags for in_range checks */
2002 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
2003 sub = true; /* subtract endOffset from current row */
2004 else
2005 sub = false; /* add it */
2006 less = true; /* normally, we want frame tail <= sum */
2007 /* If sort order is descending, flip both flags */
2008 if (!winstate->inRangeAsc)
2009 {
2010 sub = !sub;
2011 less = false;
2012 }
2013
2015 winstate->frametail_ptr);
2016 if (winstate->frametailpos == 0 &&
2017 TupIsNull(winstate->frametail_slot))
2018 {
2019 /* fetch first row into frametail_slot, if we didn't already */
2020 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2021 winstate->frametail_slot))
2022 elog(ERROR, "unexpected end of tuplestore");
2023 }
2024
2025 while (!TupIsNull(winstate->frametail_slot))
2026 {
2027 Datum tailval,
2028 currval;
2029 bool tailisnull,
2030 currisnull;
2031
2033 &tailisnull);
2035 &currisnull);
2036 if (tailisnull || currisnull)
2037 {
2038 /* order of the rows depends only on nulls_first */
2039 if (winstate->inRangeNullsFirst)
2040 {
2041 /* advance tail if tail is null or curr is not */
2042 if (!tailisnull)
2043 break;
2044 }
2045 else
2046 {
2047 /* advance tail if tail is not null or curr is null */
2048 if (!currisnull)
2049 break;
2050 }
2051 }
2052 else
2053 {
2055 winstate->inRangeColl,
2056 tailval,
2057 currval,
2058 winstate->endOffsetValue,
2059 BoolGetDatum(sub),
2060 BoolGetDatum(less))))
2061 break; /* this row is the correct frame tail */
2062 }
2063 /* Note we advance frametailpos even if the fetch fails */
2064 winstate->frametailpos++;
2065 spool_tuples(winstate, winstate->frametailpos);
2066 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2067 winstate->frametail_slot))
2068 break; /* end of partition */
2069 }
2070 winstate->frametail_valid = true;
2071 }
2072 else if (frameOptions & FRAMEOPTION_GROUPS)
2073 {
2074 /*
2075 * In GROUPS END_OFFSET mode, frame end is the last row of the
2076 * last peer group whose number satisfies the offset constraint,
2077 * and frame tail is the row after that (if any). We keep a copy
2078 * of the last-known frame tail row in frametail_slot, and advance
2079 * as necessary. Note that if we reach end of partition, we will
2080 * leave frametailpos = end+1 and frametail_slot empty.
2081 */
2082 int64 offset = DatumGetInt64(winstate->endOffsetValue);
2083 int64 maxtailgroup = 0;
2084
2085 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
2086 maxtailgroup = winstate->currentgroup - offset;
2087 else
2088 {
2089 /*
2090 * If we have an overflow, it means the target group is beyond
2091 * the range of int64. We treat this as "infinity", which
2092 * ensures the loop below advances to end of partition.
2093 */
2094 if (pg_add_s64_overflow(winstate->currentgroup, offset,
2095 &maxtailgroup))
2097 }
2098
2100 winstate->frametail_ptr);
2101 if (winstate->frametailpos == 0 &&
2102 TupIsNull(winstate->frametail_slot))
2103 {
2104 /* fetch first row into frametail_slot, if we didn't already */
2105 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2106 winstate->frametail_slot))
2107 elog(ERROR, "unexpected end of tuplestore");
2108 }
2109
2110 while (!TupIsNull(winstate->frametail_slot))
2111 {
2112 if (winstate->frametailgroup > maxtailgroup)
2113 break; /* this row is the correct frame tail */
2114 ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
2115 /* Note we advance frametailpos even if the fetch fails */
2116 winstate->frametailpos++;
2117 spool_tuples(winstate, winstate->frametailpos);
2118 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2119 winstate->frametail_slot))
2120 break; /* end of partition */
2121 if (!are_peers(winstate, winstate->temp_slot_2,
2122 winstate->frametail_slot))
2123 winstate->frametailgroup++;
2124 }
2125 ExecClearTuple(winstate->temp_slot_2);
2126 winstate->frametail_valid = true;
2127 }
2128 else
2129 Assert(false);
2130 }
2131 else
2132 Assert(false);
2133
2134 MemoryContextSwitchTo(oldcontext);
2135}

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(), fb(), 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(), WindowAggState::inRangeAsc, WindowAggState::inRangeColl, WindowAggState::inRangeNullsFirst, MemoryContextSwitchTo(), WindowAgg::ordNumCols, pg_add_s64_overflow(), PG_INT64_MAX, 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 2144 of file nodeWindowAgg.c.

2145{
2146 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2147 MemoryContext oldcontext;
2148
2149 if (winstate->grouptail_valid)
2150 return; /* already known for current row */
2151
2152 /* We may be called in a short-lived context */
2154
2155 /* If no ORDER BY, all rows are peers with each other */
2156 if (node->ordNumCols == 0)
2157 {
2158 spool_tuples(winstate, -1);
2159 winstate->grouptailpos = winstate->spooled_rows;
2160 winstate->grouptail_valid = true;
2161 MemoryContextSwitchTo(oldcontext);
2162 return;
2163 }
2164
2165 /*
2166 * Because grouptail_valid is reset only when current row advances into a
2167 * new peer group, we always reach here knowing that grouptailpos needs to
2168 * be advanced by at least one row. Hence, unlike the otherwise similar
2169 * case for frame tail tracking, we do not need persistent storage of the
2170 * group tail row.
2171 */
2172 Assert(winstate->grouptailpos <= winstate->currentpos);
2174 winstate->grouptail_ptr);
2175 for (;;)
2176 {
2177 /* Note we advance grouptailpos even if the fetch fails */
2178 winstate->grouptailpos++;
2179 spool_tuples(winstate, winstate->grouptailpos);
2180 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2181 winstate->temp_slot_2))
2182 break; /* end of partition */
2183 if (winstate->grouptailpos > winstate->currentpos &&
2184 !are_peers(winstate, winstate->temp_slot_2,
2185 winstate->ss.ss_ScanTupleSlot))
2186 break; /* this row is the group tail */
2187 }
2188 ExecClearTuple(winstate->temp_slot_2);
2189 winstate->grouptail_valid = true;
2190
2191 MemoryContextSwitchTo(oldcontext);
2192}

References are_peers(), Assert, WindowAggState::buffer, WindowAggState::currentpos, ExprContext::ecxt_per_query_memory, ExecClearTuple(), WindowAggState::grouptail_ptr, WindowAggState::grouptail_valid, WindowAggState::grouptailpos, 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 3651 of file nodeWindowAgg.c.

3654{
3655 Assert(WindowObjectIsValid(winobj));
3657 {
3658 const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
3659
3660 if (!funcname)
3661 elog(ERROR, "could not get function name");
3662 ereport(ERROR,
3664 errmsg("function %s does not allow RESPECT/IGNORE NULLS",
3665 funcname)));
3666 }
3667 else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3668 winobj->ignore_nulls = IGNORE_NULLS;
3669}
#define funcname
#define NO_NULLTREATMENT
Definition primnodes.h:589
Oid fn_oid
Definition fmgr.h:59
FmgrInfo * flinfo
Definition fmgr.h:87

References Assert, elog, ereport, errcode(), errmsg, ERROR, fb(), 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 3255 of file nodeWindowAgg.c.

3256{
3257 WindowAggState *winstate = winobj->winstate;
3258 MemoryContext oldcontext;
3259
3260 /* often called repeatedly in a row */
3262
3263 /* Don't allow passing -1 to spool_tuples here */
3264 if (pos < 0)
3265 return false;
3266
3267 /* If necessary, fetch the tuple into the spool */
3268 spool_tuples(winstate, pos);
3269
3270 if (pos >= winstate->spooled_rows)
3271 return false;
3272
3273 if (pos < winobj->markpos)
3274 elog(ERROR, "cannot fetch row before WindowObject's mark position");
3275
3277
3278 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3279
3280 /*
3281 * Advance or rewind until we are within one tuple of the one we want.
3282 */
3283 if (winobj->seekpos < pos - 1)
3284 {
3285 if (!tuplestore_skiptuples(winstate->buffer,
3286 pos - 1 - winobj->seekpos,
3287 true))
3288 elog(ERROR, "unexpected end of tuplestore");
3289 winobj->seekpos = pos - 1;
3290 }
3291 else if (winobj->seekpos > pos + 1)
3292 {
3293 if (!tuplestore_skiptuples(winstate->buffer,
3294 winobj->seekpos - (pos + 1),
3295 false))
3296 elog(ERROR, "unexpected end of tuplestore");
3297 winobj->seekpos = pos + 1;
3298 }
3299 else if (winobj->seekpos == pos)
3300 {
3301 /*
3302 * There's no API to refetch the tuple at the current position. We
3303 * have to move one tuple forward, and then one backward. (We don't
3304 * do it the other way because we might try to fetch the row before
3305 * our mark, which isn't allowed.) XXX this case could stand to be
3306 * optimized.
3307 */
3308 tuplestore_advance(winstate->buffer, true);
3309 winobj->seekpos++;
3310 }
3311
3312 /*
3313 * Now we should be on the tuple immediately before or after the one we
3314 * want, so just fetch forwards or backwards as appropriate.
3315 *
3316 * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3317 * the fetched tuple. This ensures that the slot's contents remain valid
3318 * through manipulations of the tuplestore, which some callers depend on.
3319 */
3320 if (winobj->seekpos > pos)
3321 {
3322 if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3323 elog(ERROR, "unexpected end of tuplestore");
3324 winobj->seekpos--;
3325 }
3326 else
3327 {
3328 if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3329 elog(ERROR, "unexpected end of tuplestore");
3330 winobj->seekpos++;
3331 }
3332
3333 Assert(winobj->seekpos == pos);
3334
3335 MemoryContextSwitchTo(oldcontext);
3336
3337 return true;
3338}
bool tuplestore_advance(Tuplestorestate *state, bool forward)
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)

References Assert, WindowAggState::buffer, CHECK_FOR_INTERRUPTS, ExprContext::ecxt_per_query_memory, elog, ERROR, fb(), 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 4196 of file nodeWindowAgg.c.

4197{
4198 WindowAggState *winstate;
4199 ExprContext *econtext;
4200
4201 Assert(WindowObjectIsValid(winobj));
4202 winstate = winobj->winstate;
4203
4204 econtext = winstate->ss.ps.ps_ExprContext;
4205
4206 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
4207 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4208 econtext, isnull);
4209}

References WindowObjectData::argstates, Assert, ExprContext::ecxt_outertuple, ExecEvalExpr(), fb(), 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 3997 of file nodeWindowAgg.c.

4000{
4001 WindowAggState *winstate;
4002 ExprContext *econtext;
4003 TupleTableSlot *slot;
4004 int64 abs_pos;
4006
4007 Assert(WindowObjectIsValid(winobj));
4008 winstate = winobj->winstate;
4009 econtext = winstate->ss.ps.ps_ExprContext;
4010 slot = winstate->temp_slot_1;
4011
4012 if (winobj->ignore_nulls == IGNORE_NULLS)
4014 set_mark, isnull, isout);
4015
4016 switch (seektype)
4017 {
4019 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
4020 abs_pos = mark_pos = 0; /* keep compiler quiet */
4021 break;
4022 case WINDOW_SEEK_HEAD:
4023 /* rejecting relpos < 0 is easy and simplifies code below */
4024 if (relpos < 0)
4025 goto out_of_frame;
4026 update_frameheadpos(winstate);
4027 abs_pos = winstate->frameheadpos + relpos;
4028 mark_pos = abs_pos;
4029
4030 /*
4031 * Account for exclusion option if one is active, but advance only
4032 * abs_pos not mark_pos. This prevents changes of the current
4033 * row's peer group from resulting in trying to fetch a row before
4034 * some previous mark position.
4035 *
4036 * Note that in some corner cases such as current row being
4037 * outside frame, these calculations are theoretically too simple,
4038 * but it doesn't matter because we'll end up deciding the row is
4039 * out of frame. We do not attempt to avoid fetching rows past
4040 * end of frame; that would happen in some cases anyway.
4041 */
4042 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
4043 {
4044 case 0:
4045 /* no adjustment needed */
4046 break;
4048 if (abs_pos >= winstate->currentpos &&
4049 winstate->currentpos >= winstate->frameheadpos)
4050 abs_pos++;
4051 break;
4053 update_grouptailpos(winstate);
4054 if (abs_pos >= winstate->groupheadpos &&
4055 winstate->grouptailpos > winstate->frameheadpos)
4056 {
4057 int64 overlapstart = Max(winstate->groupheadpos,
4058 winstate->frameheadpos);
4059
4060 abs_pos += winstate->grouptailpos - overlapstart;
4061 }
4062 break;
4064 update_grouptailpos(winstate);
4065 if (abs_pos >= winstate->groupheadpos &&
4066 winstate->grouptailpos > winstate->frameheadpos)
4067 {
4068 int64 overlapstart = Max(winstate->groupheadpos,
4069 winstate->frameheadpos);
4070
4071 if (abs_pos == overlapstart)
4072 abs_pos = winstate->currentpos;
4073 else
4074 abs_pos += winstate->grouptailpos - overlapstart - 1;
4075 }
4076 break;
4077 default:
4078 elog(ERROR, "unrecognized frame option state: 0x%x",
4079 winstate->frameOptions);
4080 break;
4081 }
4082 break;
4083 case WINDOW_SEEK_TAIL:
4084 /* rejecting relpos > 0 is easy and simplifies code below */
4085 if (relpos > 0)
4086 goto out_of_frame;
4087 update_frametailpos(winstate);
4088 abs_pos = winstate->frametailpos - 1 + relpos;
4089
4090 /*
4091 * Account for exclusion option if one is active. If there is no
4092 * exclusion, we can safely set the mark at the accessed row. But
4093 * if there is, we can only mark the frame start, because we can't
4094 * be sure how far back in the frame the exclusion might cause us
4095 * to fetch in future. Furthermore, we have to actually check
4096 * against frameheadpos here, since it's unsafe to try to fetch a
4097 * row before frame start if the mark might be there already.
4098 */
4099 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
4100 {
4101 case 0:
4102 /* no adjustment needed */
4103 mark_pos = abs_pos;
4104 break;
4106 if (abs_pos <= winstate->currentpos &&
4107 winstate->currentpos < winstate->frametailpos)
4108 abs_pos--;
4109 update_frameheadpos(winstate);
4110 if (abs_pos < winstate->frameheadpos)
4111 goto out_of_frame;
4112 mark_pos = winstate->frameheadpos;
4113 break;
4115 update_grouptailpos(winstate);
4116 if (abs_pos < winstate->grouptailpos &&
4117 winstate->groupheadpos < winstate->frametailpos)
4118 {
4119 int64 overlapend = Min(winstate->grouptailpos,
4120 winstate->frametailpos);
4121
4122 abs_pos -= overlapend - winstate->groupheadpos;
4123 }
4124 update_frameheadpos(winstate);
4125 if (abs_pos < winstate->frameheadpos)
4126 goto out_of_frame;
4127 mark_pos = winstate->frameheadpos;
4128 break;
4130 update_grouptailpos(winstate);
4131 if (abs_pos < winstate->grouptailpos &&
4132 winstate->groupheadpos < winstate->frametailpos)
4133 {
4134 int64 overlapend = Min(winstate->grouptailpos,
4135 winstate->frametailpos);
4136
4137 if (abs_pos == overlapend - 1)
4138 abs_pos = winstate->currentpos;
4139 else
4140 abs_pos -= overlapend - 1 - winstate->groupheadpos;
4141 }
4142 update_frameheadpos(winstate);
4143 if (abs_pos < winstate->frameheadpos)
4144 goto out_of_frame;
4145 mark_pos = winstate->frameheadpos;
4146 break;
4147 default:
4148 elog(ERROR, "unrecognized frame option state: 0x%x",
4149 winstate->frameOptions);
4150 mark_pos = 0; /* keep compiler quiet */
4151 break;
4152 }
4153 break;
4154 default:
4155 elog(ERROR, "unrecognized window seek type: %d", seektype);
4156 abs_pos = mark_pos = 0; /* keep compiler quiet */
4157 break;
4158 }
4159
4160 if (!window_gettupleslot(winobj, abs_pos, slot))
4161 goto out_of_frame;
4162
4163 /* The code above does not detect all out-of-frame cases, so check */
4164 if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
4165 goto out_of_frame;
4166
4167 if (isout)
4168 *isout = false;
4169 if (set_mark)
4171 econtext->ecxt_outertuple = slot;
4172 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4173 econtext, isnull);
4174
4176 if (isout)
4177 *isout = true;
4178 *isnull = true;
4179 return (Datum) 0;
4180}
#define Min(x, y)
Definition c.h:1091
#define Max(x, y)
Definition c.h:1085
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(), fb(), 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 3824 of file nodeWindowAgg.c.

3827{
3828 WindowAggState *winstate;
3829 int64 abs_pos;
3831 Datum datum;
3832 bool null_treatment;
3833 int notnull_offset;
3834 int notnull_relpos;
3835 int forward;
3836 bool myisout;
3837 bool got_datum;
3838
3839 Assert(WindowObjectIsValid(winobj));
3840 winstate = winobj->winstate;
3841
3842 null_treatment = (winobj->ignore_nulls == IGNORE_NULLS && relpos != 0);
3843
3844 switch (seektype)
3845 {
3847 if (null_treatment)
3848 abs_pos = winstate->currentpos;
3849 else
3850 abs_pos = winstate->currentpos + relpos;
3851 break;
3852 case WINDOW_SEEK_HEAD:
3853 if (null_treatment)
3854 abs_pos = 0;
3855 else
3856 abs_pos = relpos;
3857 break;
3858 case WINDOW_SEEK_TAIL:
3859 spool_tuples(winstate, -1);
3860 abs_pos = winstate->spooled_rows - 1 + relpos;
3861 break;
3862 default:
3863 elog(ERROR, "unrecognized window seek type: %d", seektype);
3864 abs_pos = 0; /* keep compiler quiet */
3865 break;
3866 }
3867
3868 /* Easy case if IGNORE NULLS is not specified */
3869 if (!null_treatment)
3870 {
3871 /* get tuple and evaluate in partition */
3872 datum = gettuple_eval_partition(winobj, argno,
3873 abs_pos, isnull, &myisout);
3874 if (!myisout && set_mark)
3875 WinSetMarkPosition(winobj, abs_pos);
3876 if (isout)
3877 *isout = myisout;
3878 return datum;
3879 }
3880
3881 /* Prepare for loop */
3882 notnull_offset = 0;
3884 forward = relpos > 0 ? 1 : -1;
3885 myisout = false;
3886 got_datum = false;
3887 datum = 0;
3888
3889 /*
3890 * IGNORE NULLS + WINDOW_SEEK_CURRENT + relpos > 0 case, we would fetch
3891 * beyond the current row + relpos to find out the target row. If we mark
3892 * at abs_pos, next call to WinGetFuncArgInPartition or
3893 * WinGetFuncArgInFrame (in case when a window function have multiple
3894 * args) could fail with "cannot fetch row before WindowObject's mark
3895 * position". So keep the mark position at currentpos.
3896 */
3897 if (seektype == WINDOW_SEEK_CURRENT && relpos > 0)
3898 mark_pos = winstate->currentpos;
3899 else
3900 {
3901 /*
3902 * For other cases we have no idea what position of row callers would
3903 * fetch next time. Also for relpos < 0 case (we go backward), we
3904 * cannot set mark either. For those cases we always set mark at 0.
3905 */
3906 mark_pos = 0;
3907 }
3908
3909 /*
3910 * Get the next nonnull value in the partition, moving forward or backward
3911 * until we find a value or reach the partition's end. We cache the
3912 * nullness status because we may repeat this process many times.
3913 */
3914 do
3915 {
3916 int nn_info; /* NOT NULL status */
3917
3918 abs_pos += forward;
3919 if (abs_pos < 0) /* clearly out of partition */
3920 break;
3921
3922 /* check NOT NULL cached info */
3924 if (nn_info == NN_NOTNULL) /* this row is known to be NOT NULL */
3926 else if (nn_info == NN_NULL) /* this row is known to be NULL */
3927 continue; /* keep on moving forward or backward */
3928 else /* need to check NULL or not */
3929 {
3930 /*
3931 * NOT NULL info does not exist yet. Get tuple and evaluate func
3932 * arg in partition. Keep the return value in case this row is the
3933 * target; re-evaluating a volatile argument could give a
3934 * different nullness status.
3935 */
3936 datum = gettuple_eval_partition(winobj, argno,
3937 abs_pos, isnull, &myisout);
3938 if (myisout) /* out of partition? */
3939 break;
3940 if (!*isnull)
3941 {
3944 got_datum = true;
3945 }
3946 /* record the row status */
3947 put_notnull_info(winobj, abs_pos, argno, *isnull);
3948 }
3949 } while (notnull_offset < notnull_relpos);
3950
3951 /* get tuple and evaluate func arg in partition */
3952 if (!got_datum)
3953 datum = gettuple_eval_partition(winobj, argno,
3954 abs_pos, isnull, &myisout);
3955 if (!myisout && set_mark)
3957 if (isout)
3958 *isout = myisout;
3959
3960 return datum;
3961}
static Datum gettuple_eval_partition(WindowObject winobj, int argno, int64 abs_pos, bool *isnull, bool *isout)

References Assert, WindowAggState::currentpos, elog, ERROR, fb(), 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 3684 of file nodeWindowAgg.c.

3685{
3686 Assert(WindowObjectIsValid(winobj));
3687 if (winobj->localmem == NULL)
3688 winobj->localmem =
3690 return winobj->localmem;
3691}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1269

References Assert, fb(), 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)

Definition at line 3714 of file nodeWindowAgg.c.

3715{
3716 Assert(WindowObjectIsValid(winobj));
3717 spool_tuples(winobj->winstate, -1);
3718 return winobj->winstate->spooled_rows;
3719}

References Assert, spool_tuples(), WindowAggState::spooled_rows, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by window_cume_dist(), window_ntile(), and window_percent_rank().

◆ WinRowsArePeers()

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

Definition at line 3767 of file nodeWindowAgg.c.

3768{
3769 WindowAggState *winstate;
3770 WindowAgg *node;
3773 bool res;
3774
3775 Assert(WindowObjectIsValid(winobj));
3776 winstate = winobj->winstate;
3777 node = (WindowAgg *) winstate->ss.ps.plan;
3778
3779 /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3780 if (node->ordNumCols == 0)
3781 return true;
3782
3783 /*
3784 * Note: OK to use temp_slot_2 here because we aren't calling any
3785 * frame-related functions (those tend to clobber temp_slot_2).
3786 */
3787 slot1 = winstate->temp_slot_1;
3788 slot2 = winstate->temp_slot_2;
3789
3790 if (!window_gettupleslot(winobj, pos1, slot1))
3791 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3792 pos1);
3793 if (!window_gettupleslot(winobj, pos2, slot2))
3794 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3795 pos2);
3796
3797 res = are_peers(winstate, slot1, slot2);
3798
3801
3802 return res;
3803}
#define INT64_FORMAT
Definition c.h:634

References are_peers(), Assert, elog, ERROR, ExecClearTuple(), fb(), 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 3732 of file nodeWindowAgg.c.

3733{
3734 WindowAggState *winstate;
3735
3736 Assert(WindowObjectIsValid(winobj));
3737 winstate = winobj->winstate;
3738
3739 if (markpos < winobj->markpos)
3740 elog(ERROR, "cannot move WindowObject's mark position backward");
3741 tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3742 if (markpos > winobj->markpos)
3743 {
3744 tuplestore_skiptuples(winstate->buffer,
3745 markpos - winobj->markpos,
3746 true);
3747 winobj->markpos = markpos;
3748 }
3749 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3750 if (markpos > winobj->seekpos)
3751 {
3752 tuplestore_skiptuples(winstate->buffer,
3753 markpos - winobj->seekpos,
3754 true);
3755 winobj->seekpos = markpos;
3756 }
3757}

References Assert, WindowAggState::buffer, elog, ERROR, fb(), 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().