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 238 of file nodeWindowAgg.c.

◆ NN_BYTES_TO_POS

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

Definition at line 244 of file nodeWindowAgg.c.

◆ NN_ITEM_PER_VAR

#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)

Definition at line 240 of file nodeWindowAgg.c.

◆ NN_MASK

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

Definition at line 237 of file nodeWindowAgg.c.

◆ NN_NOTNULL

#define NN_NOTNULL   0x02 /* NOT NULL */

Definition at line 236 of file nodeWindowAgg.c.

◆ NN_NULL

#define NN_NULL   0x01 /* NULL */

Definition at line 235 of file nodeWindowAgg.c.

◆ NN_POS_TO_BYTES

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

Definition at line 242 of file nodeWindowAgg.c.

◆ NN_SHIFT

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

Definition at line 246 of file nodeWindowAgg.c.

◆ NN_UNKNOWN

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

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

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

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 465 of file nodeWindowAgg.c.

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

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 3228 of file nodeWindowAgg.c.

3230{
3231 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3232 ExprContext *econtext = winstate->tmpcontext;
3233
3234 /* If no ORDER BY, all rows are peers with each other */
3235 if (node->ordNumCols == 0)
3236 return true;
3237
3238 econtext->ecxt_outertuple = slot1;
3239 econtext->ecxt_innertuple = slot2;
3240 return ExecQualAndReset(winstate->ordEqfunction, econtext);
3241}
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition executor.h:554
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:289
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:291
Plan * plan
Definition execnodes.h:1202
PlanState ps
Definition execnodes.h:1662
ScanState ss
Definition execnodes.h:2532
ExprState * ordEqfunction
Definition execnodes.h:2542

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 1240 of file nodeWindowAgg.c.

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

2200{
2201 WindowAggState *winstate = castNode(WindowAggState, pstate);
2202 ExprContext *econtext;
2203 int frameOptions = winstate->frameOptions;
2204 Datum value;
2205 bool isnull;
2206 int16 len;
2207 bool byval;
2208
2209 /* Ensure we've not been called before for this scan */
2210 Assert(winstate->all_first);
2211
2212 econtext = winstate->ss.ps.ps_ExprContext;
2213
2214 if (frameOptions & FRAMEOPTION_START_OFFSET)
2215 {
2216 Assert(winstate->startOffset != NULL);
2218 econtext,
2219 &isnull);
2220 if (isnull)
2221 ereport(ERROR,
2223 errmsg("frame starting offset must not be null")));
2224 /* copy value into query-lifespan context */
2226 &len,
2227 &byval);
2228 winstate->startOffsetValue = datumCopy(value, byval, len);
2229 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2230 {
2231 /* value is known to be int8 */
2232 int64 offset = DatumGetInt64(value);
2233
2234 if (offset < 0)
2235 ereport(ERROR,
2237 errmsg("frame starting offset must not be negative")));
2238 }
2239 }
2240
2241 if (frameOptions & FRAMEOPTION_END_OFFSET)
2242 {
2243 Assert(winstate->endOffset != NULL);
2245 econtext,
2246 &isnull);
2247 if (isnull)
2248 ereport(ERROR,
2250 errmsg("frame ending offset must not be null")));
2251 /* copy value into query-lifespan context */
2252 get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2253 &len,
2254 &byval);
2255 winstate->endOffsetValue = datumCopy(value, byval, len);
2256 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2257 {
2258 /* value is known to be int8 */
2259 int64 offset = DatumGetInt64(value);
2260
2261 if (offset < 0)
2262 ereport(ERROR,
2264 errmsg("frame ending offset must not be negative")));
2265 }
2266 }
2267 winstate->all_first = false;
2268}
int64_t int64
Definition c.h:680
int16_t int16
Definition c.h:678
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:444
static struct @175 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2565
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition nodes.h:180
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:634
#define FRAMEOPTION_START_OFFSET
Definition parsenodes.h:632
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:616
#define FRAMEOPTION_ROWS
Definition parsenodes.h:615
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:1243
ExprState * endOffset
Definition execnodes.h:2560
Datum startOffsetValue
Definition execnodes.h:2561
Datum endOffsetValue
Definition execnodes.h:2562
ExprState * startOffset
Definition execnodes.h:2559

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 709 of file nodeWindowAgg.c.

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

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 1080 of file nodeWindowAgg.c.

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

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 2858 of file nodeWindowAgg.c.

2859{
2861 int i;
2862
2863 if (node->buffer != NULL)
2864 {
2865 tuplestore_end(node->buffer);
2866
2867 /* nullify so that release_partition skips the tuplestore_clear() */
2868 node->buffer = NULL;
2869 }
2870
2871 release_partition(node);
2872
2873 for (i = 0; i < node->numaggs; i++)
2874 {
2875 if (node->peragg[i].aggcontext != node->aggcontext)
2877 }
2880
2881 pfree(node->perfunc);
2882 pfree(node->peragg);
2883
2884 outerPlan = outerPlanState(node);
2886}
void ExecEndNode(PlanState *node)
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition execnodes.h:2589
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 2548 of file nodeWindowAgg.c.

2549{
2550 WindowAggState *winstate;
2551 Plan *outerPlan;
2552 ExprContext *econtext;
2553 ExprContext *tmpcontext;
2554 WindowStatePerFunc perfunc;
2555 WindowStatePerAgg peragg;
2556 int frameOptions = node->frameOptions;
2557 int numfuncs,
2558 wfuncno,
2559 numaggs,
2560 aggno;
2562 ListCell *l;
2563
2564 /* check for unsupported flags */
2565 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2566
2567 /*
2568 * create state structure
2569 */
2570 winstate = makeNode(WindowAggState);
2571 winstate->ss.ps.plan = (Plan *) node;
2572 winstate->ss.ps.state = estate;
2573 winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2574
2575 /* copy frame options to state node for easy access */
2576 winstate->frameOptions = frameOptions;
2577
2578 /*
2579 * Create expression contexts. We need two, one for per-input-tuple
2580 * processing and one for per-output-tuple processing. We cheat a little
2581 * by using ExecAssignExprContext() to build both.
2582 */
2583 ExecAssignExprContext(estate, &winstate->ss.ps);
2584 tmpcontext = winstate->ss.ps.ps_ExprContext;
2585 winstate->tmpcontext = tmpcontext;
2586 ExecAssignExprContext(estate, &winstate->ss.ps);
2587
2588 /* Create long-lived context for storage of partition-local memory etc */
2589 winstate->partcontext =
2591 "WindowAgg Partition",
2593
2594 /*
2595 * Create mid-lived context for aggregate trans values etc.
2596 *
2597 * Note that moving aggregates each use their own private context, not
2598 * this one.
2599 */
2600 winstate->aggcontext =
2602 "WindowAgg Aggregates",
2604
2605 /* Only the top-level WindowAgg may have a qual */
2606 Assert(node->plan.qual == NIL || node->topWindow);
2607
2608 /* Initialize the qual */
2609 winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2610 (PlanState *) winstate);
2611
2612 /*
2613 * Setup the run condition, if we received one from the query planner.
2614 * When set, this may allow us to move into pass-through mode so that we
2615 * don't have to perform any further evaluation of WindowFuncs in the
2616 * current partition or possibly stop returning tuples altogether when all
2617 * tuples are in the same partition.
2618 */
2619 winstate->runcondition = ExecInitQual(node->runCondition,
2620 (PlanState *) winstate);
2621
2622 /*
2623 * When we're not the top-level WindowAgg node or we are but have a
2624 * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2625 * modes when the runCondition becomes false.
2626 */
2627 winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2628
2629 /* remember if we're the top-window or we are below the top-window */
2630 winstate->top_window = node->topWindow;
2631
2632 /*
2633 * initialize child nodes
2634 */
2635 outerPlan = outerPlan(node);
2636 outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2637
2638 /*
2639 * initialize source tuple type (which is also the tuple type that we'll
2640 * store in the tuplestore and use in all our working slots).
2641 */
2644
2645 /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2646 winstate->ss.ps.outeropsset = true;
2647 winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2648 winstate->ss.ps.outeropsfixed = true;
2649
2650 /*
2651 * tuple table initialization
2652 */
2655 winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2657 winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2659 winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2661
2662 /*
2663 * create frame head and tail slots only if needed (must create slots in
2664 * exactly the same cases that update_frameheadpos and update_frametailpos
2665 * need them)
2666 */
2667 winstate->framehead_slot = winstate->frametail_slot = NULL;
2668
2669 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2670 {
2671 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2672 node->ordNumCols != 0) ||
2673 (frameOptions & FRAMEOPTION_START_OFFSET))
2676 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2677 node->ordNumCols != 0) ||
2678 (frameOptions & FRAMEOPTION_END_OFFSET))
2681 }
2682
2683 /*
2684 * Initialize result slot, type and projection.
2685 */
2687 ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2688
2689 /* Set up data for comparing tuples */
2690 if (node->partNumCols > 0)
2691 winstate->partEqfunction =
2693 node->partNumCols,
2694 node->partColIdx,
2695 node->partOperators,
2696 node->partCollations,
2697 &winstate->ss.ps);
2698
2699 if (node->ordNumCols > 0)
2700 winstate->ordEqfunction =
2702 node->ordNumCols,
2703 node->ordColIdx,
2704 node->ordOperators,
2705 node->ordCollations,
2706 &winstate->ss.ps);
2707
2708 /*
2709 * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2710 */
2711 numfuncs = winstate->numfuncs;
2712 numaggs = winstate->numaggs;
2713 econtext = winstate->ss.ps.ps_ExprContext;
2714 econtext->ecxt_aggvalues = palloc0_array(Datum, numfuncs);
2715 econtext->ecxt_aggnulls = palloc0_array(bool, numfuncs);
2716
2717 /*
2718 * allocate per-wfunc/per-agg state information.
2719 */
2720 perfunc = palloc0_array(WindowStatePerFuncData, numfuncs);
2721 peragg = palloc0_array(WindowStatePerAggData, numaggs);
2722 winstate->perfunc = perfunc;
2723 winstate->peragg = peragg;
2724
2725 wfuncno = -1;
2726 aggno = -1;
2727 foreach(l, winstate->funcs)
2728 {
2729 WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2730 WindowFunc *wfunc = wfuncstate->wfunc;
2733 int i;
2734
2735 if (wfunc->winref != node->winref) /* planner screwed up? */
2736 elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2737 wfunc->winref, node->winref);
2738
2739 /* Look for a previous duplicate window function */
2740 for (i = 0; i <= wfuncno; i++)
2741 {
2742 if (equal(wfunc, perfunc[i].wfunc) &&
2743 !contain_volatile_functions((Node *) wfunc))
2744 break;
2745 }
2746 if (i <= wfuncno)
2747 {
2748 /* Found a match to an existing entry, so just mark it */
2749 wfuncstate->wfuncno = i;
2750 continue;
2751 }
2752
2753 /* Nope, so assign a new PerAgg record */
2754 perfuncstate = &perfunc[++wfuncno];
2755
2756 /* Mark WindowFunc state node with assigned index in the result array */
2757 wfuncstate->wfuncno = wfuncno;
2758
2759 /* Check permission to call window function */
2761 ACL_EXECUTE);
2762 if (aclresult != ACLCHECK_OK)
2764 get_func_name(wfunc->winfnoid));
2765 InvokeFunctionExecuteHook(wfunc->winfnoid);
2766
2767 /* Fill in the perfuncstate data */
2768 perfuncstate->wfuncstate = wfuncstate;
2769 perfuncstate->wfunc = wfunc;
2770 perfuncstate->numArguments = list_length(wfuncstate->args);
2771 perfuncstate->winCollation = wfunc->inputcollid;
2772
2773 get_typlenbyval(wfunc->wintype,
2774 &perfuncstate->resulttypeLen,
2775 &perfuncstate->resulttypeByVal);
2776
2777 /*
2778 * If it's really just a plain aggregate function, we'll emulate the
2779 * Agg environment for it.
2780 */
2781 perfuncstate->plain_agg = wfunc->winagg;
2782 if (wfunc->winagg)
2783 {
2785
2786 perfuncstate->aggno = ++aggno;
2787 peraggstate = &winstate->peragg[aggno];
2788 initialize_peragg(winstate, wfunc, peraggstate);
2789 peraggstate->wfuncno = wfuncno;
2790 }
2791 else
2792 {
2794
2795 winobj->winstate = winstate;
2796 winobj->argstates = wfuncstate->args;
2797 winobj->localmem = NULL;
2798 perfuncstate->winobj = winobj;
2799 winobj->ignore_nulls = wfunc->ignore_nulls;
2801
2802 /* It's a real window function, so set up to call it. */
2803 fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2804 econtext->ecxt_per_query_memory);
2805 fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2806 }
2807 }
2808
2809 /* Update numfuncs, numaggs to match number of unique functions found */
2810 winstate->numfuncs = wfuncno + 1;
2811 winstate->numaggs = aggno + 1;
2812
2813 /* Set up WindowObject for aggregates, if needed */
2814 if (winstate->numaggs > 0)
2815 {
2817
2818 agg_winobj->winstate = winstate;
2819 agg_winobj->argstates = NIL;
2820 agg_winobj->localmem = NULL;
2821 /* make sure markptr = -1 to invalidate. It may not get used */
2822 agg_winobj->markptr = -1;
2823 agg_winobj->readptr = -1;
2824 winstate->agg_winobj = agg_winobj;
2825 }
2826
2827 /* Set the status to running */
2828 winstate->status = WINDOWAGG_RUN;
2829
2830 /* initialize frame bound offset expressions */
2831 winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2832 (PlanState *) winstate);
2833 winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2834 (PlanState *) winstate);
2835
2836 /* Lookup in_range support functions if needed */
2837 if (OidIsValid(node->startInRangeFunc))
2838 fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2839 if (OidIsValid(node->endInRangeFunc))
2840 fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2841 winstate->inRangeColl = node->inRangeColl;
2842 winstate->inRangeAsc = node->inRangeAsc;
2843 winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2844
2845 winstate->all_first = true;
2846 winstate->partition_spooled = false;
2847 winstate->more_partitions = false;
2848 winstate->next_partition = true;
2849
2850 return winstate;
2851}
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:3902
bool contain_volatile_functions(Node *clause)
Definition clauses.c:567
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:229
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
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:2524
#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:1922
#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:159
#define InvokeFunctionExecuteHook(objectId)
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:622
@ OBJECT_FUNCTION
#define FRAMEOPTION_RANGE
Definition parsenodes.h:614
#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:1287
const TupleTableSlotOps * outerops
Definition execnodes.h:1279
ExprState * qual
Definition execnodes.h:1225
bool outeropsfixed
Definition execnodes.h:1283
EState * state
Definition execnodes.h:1204
ExecProcNodeMtd ExecProcNode
Definition execnodes.h:1208
List * qual
Definition plannodes.h:237
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1665
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
FmgrInfo endInRangeFunc
Definition execnodes.h:2566
FmgrInfo startInRangeFunc
Definition execnodes.h:2565
ExprState * runcondition
Definition execnodes.h:2577
TupleTableSlot * temp_slot_2
Definition execnodes.h:2615
WindowAggStatus status
Definition execnodes.h:2556
bool inRangeNullsFirst
Definition execnodes.h:2569
ExprState * partEqfunction
Definition execnodes.h:2541
bool use_pass_through
Definition execnodes.h:2572
int partNumCols
Definition plannodes.h:1264
Oid endInRangeFunc
Definition plannodes.h:1308
Node * endOffset
Definition plannodes.h:1294
bool topWindow
Definition plannodes.h:1323
Oid inRangeColl
Definition plannodes.h:1311
Node * startOffset
Definition plannodes.h:1291
List * runCondition
Definition plannodes.h:1297
Oid startInRangeFunc
Definition plannodes.h:1305
bool inRangeAsc
Definition plannodes.h:1314
Index winref
Definition plannodes.h:1261
bool inRangeNullsFirst
Definition plannodes.h:1317
int ordNumCols
Definition plannodes.h:1276
int frameOptions
Definition plannodes.h:1288
WindowFunc * wfunc
Definition execnodes.h:960
Index winref
Definition primnodes.h:604
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 2893 of file nodeWindowAgg.c.

2894{
2896 ExprContext *econtext = node->ss.ps.ps_ExprContext;
2897
2898 node->status = WINDOWAGG_RUN;
2899 node->all_first = true;
2900
2901 /* release tuplestore et al */
2902 release_partition(node);
2903
2904 /* release all temp tuples, but especially first_part_slot */
2910 if (node->framehead_slot)
2912 if (node->frametail_slot)
2914
2915 /* Forget current wfunc values */
2916 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2917 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2918
2919 /*
2920 * if chgParam of subnode is not null then plan will be re-scanned by
2921 * first ExecProcNode.
2922 */
2923 if (outerPlan->chgParam == NULL)
2925}
#define MemSet(start, val, len)
Definition c.h:1147
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 2280 of file nodeWindowAgg.c.

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

632{
634
636
637 /*
638 * Apply the agg's finalfn if one is provided, else return transValue.
639 */
640 if (OidIsValid(peraggstate->finalfn_oid))
641 {
643 int numFinalArgs = peraggstate->numFinalArgs;
644 bool anynull;
645 int i;
646
648 numFinalArgs,
649 perfuncstate->winCollation,
650 (Node *) winstate, NULL);
651 fcinfo->args[0].value =
653 peraggstate->transValueIsNull,
654 peraggstate->transtypeLen);
655 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
656 anynull = peraggstate->transValueIsNull;
657
658 /* Fill any remaining argument positions with nulls */
659 for (i = 1; i < numFinalArgs; i++)
660 {
661 fcinfo->args[i].value = (Datum) 0;
662 fcinfo->args[i].isnull = true;
663 anynull = true;
664 }
665
666 if (fcinfo->flinfo->fn_strict && anynull)
667 {
668 /* don't call a strict function with NULL inputs */
669 *result = (Datum) 0;
670 *isnull = true;
671 }
672 else
673 {
674 Datum res;
675
676 winstate->curaggcontext = peraggstate->aggcontext;
677 res = FunctionCallInvoke(fcinfo);
678 winstate->curaggcontext = NULL;
679 *isnull = fcinfo->isnull;
681 fcinfo->isnull,
682 peraggstate->resulttypeLen);
683 }
684 }
685 else
686 {
687 *result =
689 peraggstate->transValueIsNull,
690 peraggstate->transtypeLen);
691 *isnull = peraggstate->transValueIsNull;
692 }
693
695}
#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 3589 of file nodeWindowAgg.c.

3590{
3591 uint8 *mbp;
3592 uint8 mb;
3593 int64 bpos;
3594
3595 if (!winobj->notnull_info_cacheable[argno])
3596 return NN_UNKNOWN;
3597
3598 grow_notnull_info(winobj, pos, argno);
3599 bpos = NN_POS_TO_BYTES(pos);
3600 mbp = winobj->notnull_info[argno];
3601 mb = mbp[bpos];
3602 return (mb >> (NN_SHIFT(pos))) & NN_MASK;
3603}
uint8_t uint8
Definition c.h:681
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 3206 of file nodeWindowAgg.c.

3207{
3208 Oid typinput,
3209 typioparam;
3210 char *strInitVal;
3211 Datum initVal;
3212
3213 getTypeInputInfo(transtype, &typinput, &typioparam);
3216 typioparam, -1);
3218 return initVal;
3219}
#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:3190
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 3342 of file nodeWindowAgg.c.

3344{
3345 WindowAggState *winstate;
3346 ExprContext *econtext;
3347 TupleTableSlot *slot;
3348
3349 winstate = winobj->winstate;
3350 slot = winstate->temp_slot_1;
3351 if (!window_gettupleslot(winobj, abs_pos, slot))
3352 {
3353 /* out of partition */
3354 if (isout)
3355 *isout = true;
3356 *isnull = true;
3357 return (Datum) 0;
3358 }
3359
3360 if (isout)
3361 *isout = false;
3362 econtext = winstate->ss.ps.ps_ExprContext;
3363 econtext->ecxt_outertuple = slot;
3365 (winobj->argstates, argno),
3366 econtext, isnull);
3367}
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 3545 of file nodeWindowAgg.c.

3546{
3547/* initial number of notnull info members */
3548#define INIT_NOT_NULL_INFO_NUM 128
3549
3550 if (pos >= winobj->num_notnull_info[argno])
3551 {
3552 /* We may be called in a short-lived context */
3555
3556 for (;;)
3557 {
3559 (winobj->num_notnull_info[argno]);
3560 Size newsize;
3561
3562 if (oldsize == 0) /* memory has not been allocated yet for this
3563 * arg */
3564 {
3566 winobj->notnull_info[argno] = palloc0(newsize);
3567 }
3568 else
3569 {
3570 newsize = oldsize * 2;
3571 winobj->notnull_info[argno] =
3573 }
3575 if (winobj->num_notnull_info[argno] > pos)
3576 break;
3577 }
3578 MemoryContextSwitchTo(oldcontext);
3579 }
3580}
size_t Size
Definition c.h:748
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 3375 of file nodeWindowAgg.c.

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

3513{
3514 int numargs = perfuncstate->numArguments;
3515
3516 if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3517 {
3518 int argno = 0;
3519 ListCell *lc;
3520
3521 winobj->notnull_info = palloc0_array(uint8 *, numargs);
3522 winobj->num_notnull_info = palloc0_array(int64, numargs);
3523 winobj->notnull_info_cacheable = palloc_array(bool, numargs);
3524
3525 foreach(lc, perfuncstate->wfunc->args)
3526 {
3527 Node *arg = (Node *) lfirst(lc);
3528
3529 winobj->notnull_info_cacheable[argno] =
3532
3533 argno++;
3534 }
3535 }
3536}
bool contain_subplans(Node *clause)
Definition clauses.c:359
#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 2933 of file nodeWindowAgg.c.

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

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

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 1132 of file nodeWindowAgg.c.

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

3614{
3615 uint8 *mbp;
3616 uint8 mb;
3617 int64 bpos;
3618 uint8 val = isnull ? NN_NULL : NN_NOTNULL;
3619 int shift;
3620
3621 if (!winobj->notnull_info_cacheable[argno])
3622 return;
3623
3624 grow_notnull_info(winobj, pos, argno);
3625 bpos = NN_POS_TO_BYTES(pos);
3626 mbp = winobj->notnull_info[argno];
3627 mb = mbp[bpos];
3628 shift = NN_SHIFT(pos);
3629 mb &= ~(NN_MASK << shift); /* clear map */
3630 mb |= (val << shift); /* update map */
3631 mbp[bpos] = mb;
3632}
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 1439 of file nodeWindowAgg.c.

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

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

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 1345 of file nodeWindowAgg.c.

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

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

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

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 2143 of file nodeWindowAgg.c.

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

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 3647 of file nodeWindowAgg.c.

3650{
3651 Assert(WindowObjectIsValid(winobj));
3653 {
3654 const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
3655
3656 if (!funcname)
3657 elog(ERROR, "could not get function name");
3658 ereport(ERROR,
3660 errmsg("function %s does not allow RESPECT/IGNORE NULLS",
3661 funcname)));
3662 }
3663 else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3664 winobj->ignore_nulls = IGNORE_NULLS;
3665}
#define funcname
#define NO_NULLTREATMENT
Definition primnodes.h:576
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 3251 of file nodeWindowAgg.c.

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

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

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 3993 of file nodeWindowAgg.c.

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

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

3681{
3682 Assert(WindowObjectIsValid(winobj));
3683 if (winobj->localmem == NULL)
3684 winobj->localmem =
3686 return winobj->localmem;
3687}
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 3710 of file nodeWindowAgg.c.

3711{
3712 Assert(WindowObjectIsValid(winobj));
3713 spool_tuples(winobj->winstate, -1);
3714 return winobj->winstate->spooled_rows;
3715}

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 3763 of file nodeWindowAgg.c.

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

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 3728 of file nodeWindowAgg.c.

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

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().