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

◆ NN_BYTES_TO_POS

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

Definition at line 243 of file nodeWindowAgg.c.

◆ NN_ITEM_PER_VAR

#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)

Definition at line 239 of file nodeWindowAgg.c.

◆ NN_MASK

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

Definition at line 236 of file nodeWindowAgg.c.

◆ NN_NOTNULL

#define NN_NOTNULL   0x02 /* NOT NULL */

Definition at line 235 of file nodeWindowAgg.c.

◆ NN_NULL

#define NN_NULL   0x01 /* NULL */

Definition at line 234 of file nodeWindowAgg.c.

◆ NN_POS_TO_BYTES

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

Definition at line 241 of file nodeWindowAgg.c.

◆ NN_SHIFT

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

Definition at line 245 of file nodeWindowAgg.c.

◆ NN_UNKNOWN

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

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

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

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

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

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

3185{
3186 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3187 ExprContext *econtext = winstate->tmpcontext;
3188
3189 /* If no ORDER BY, all rows are peers with each other */
3190 if (node->ordNumCols == 0)
3191 return true;
3192
3193 econtext->ecxt_outertuple = slot1;
3194 econtext->ecxt_innertuple = slot2;
3195 return ExecQualAndReset(winstate->ordEqfunction, econtext);
3196}
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition executor.h:549
TupleTableSlot * ecxt_innertuple
Definition execnodes.h:286
TupleTableSlot * ecxt_outertuple
Definition execnodes.h:288
Plan * plan
Definition execnodes.h:1177
PlanState ps
Definition execnodes.h:1633
ScanState ss
Definition execnodes.h:2501
ExprState * ordEqfunction
Definition execnodes.h:2511

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

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

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

2152{
2153 WindowAggState *winstate = castNode(WindowAggState, pstate);
2154 ExprContext *econtext;
2155 int frameOptions = winstate->frameOptions;
2156 Datum value;
2157 bool isnull;
2158 int16 len;
2159 bool byval;
2160
2161 /* Ensure we've not been called before for this scan */
2162 Assert(winstate->all_first);
2163
2164 econtext = winstate->ss.ps.ps_ExprContext;
2165
2166 if (frameOptions & FRAMEOPTION_START_OFFSET)
2167 {
2168 Assert(winstate->startOffset != NULL);
2170 econtext,
2171 &isnull);
2172 if (isnull)
2173 ereport(ERROR,
2175 errmsg("frame starting offset must not be null")));
2176 /* copy value into query-lifespan context */
2178 &len,
2179 &byval);
2180 winstate->startOffsetValue = datumCopy(value, byval, len);
2181 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2182 {
2183 /* value is known to be int8 */
2184 int64 offset = DatumGetInt64(value);
2185
2186 if (offset < 0)
2187 ereport(ERROR,
2189 errmsg("frame starting offset must not be negative")));
2190 }
2191 }
2192
2193 if (frameOptions & FRAMEOPTION_END_OFFSET)
2194 {
2195 Assert(winstate->endOffset != NULL);
2197 econtext,
2198 &isnull);
2199 if (isnull)
2200 ereport(ERROR,
2202 errmsg("frame ending offset must not be null")));
2203 /* copy value into query-lifespan context */
2204 get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2205 &len,
2206 &byval);
2207 winstate->endOffsetValue = datumCopy(value, byval, len);
2208 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2209 {
2210 /* value is known to be int8 */
2211 int64 offset = DatumGetInt64(value);
2212
2213 if (offset < 0)
2214 ereport(ERROR,
2216 errmsg("frame ending offset must not be negative")));
2217 }
2218 }
2219 winstate->all_first = false;
2220}
int64_t int64
Definition c.h:615
int16_t int16
Definition c.h:613
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:439
static struct @174 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2471
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition nodes.h:182
#define FRAMEOPTION_END_OFFSET
Definition parsenodes.h:630
#define FRAMEOPTION_START_OFFSET
Definition parsenodes.h:628
#define FRAMEOPTION_GROUPS
Definition parsenodes.h:612
#define FRAMEOPTION_ROWS
Definition parsenodes.h:611
const void size_t len
static int64 DatumGetInt64(Datum X)
Definition postgres.h:403
Expr * expr
Definition execnodes.h:129
ExprContext * ps_ExprContext
Definition execnodes.h:1216
ExprState * endOffset
Definition execnodes.h:2529
Datum startOffsetValue
Definition execnodes.h:2530
Datum endOffsetValue
Definition execnodes.h:2531
ExprState * startOffset
Definition execnodes.h:2528

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

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

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

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

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, and WindowAggState::ss.

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2813 of file nodeWindowAgg.c.

2814{
2816 int i;
2817
2818 if (node->buffer != NULL)
2819 {
2820 tuplestore_end(node->buffer);
2821
2822 /* nullify so that release_partition skips the tuplestore_clear() */
2823 node->buffer = NULL;
2824 }
2825
2826 release_partition(node);
2827
2828 for (i = 0; i < node->numaggs; i++)
2829 {
2830 if (node->peragg[i].aggcontext != node->aggcontext)
2832 }
2835
2836 pfree(node->perfunc);
2837 pfree(node->peragg);
2838
2839 outerPlan = outerPlanState(node);
2841}
void ExecEndNode(PlanState *node)
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition execnodes.h:2558
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 2500 of file nodeWindowAgg.c.

2501{
2502 WindowAggState *winstate;
2503 Plan *outerPlan;
2504 ExprContext *econtext;
2505 ExprContext *tmpcontext;
2506 WindowStatePerFunc perfunc;
2507 WindowStatePerAgg peragg;
2508 int frameOptions = node->frameOptions;
2509 int numfuncs,
2510 wfuncno,
2511 numaggs,
2512 aggno;
2514 ListCell *l;
2515
2516 /* check for unsupported flags */
2517 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2518
2519 /*
2520 * create state structure
2521 */
2522 winstate = makeNode(WindowAggState);
2523 winstate->ss.ps.plan = (Plan *) node;
2524 winstate->ss.ps.state = estate;
2525 winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2526
2527 /* copy frame options to state node for easy access */
2528 winstate->frameOptions = frameOptions;
2529
2530 /*
2531 * Create expression contexts. We need two, one for per-input-tuple
2532 * processing and one for per-output-tuple processing. We cheat a little
2533 * by using ExecAssignExprContext() to build both.
2534 */
2535 ExecAssignExprContext(estate, &winstate->ss.ps);
2536 tmpcontext = winstate->ss.ps.ps_ExprContext;
2537 winstate->tmpcontext = tmpcontext;
2538 ExecAssignExprContext(estate, &winstate->ss.ps);
2539
2540 /* Create long-lived context for storage of partition-local memory etc */
2541 winstate->partcontext =
2543 "WindowAgg Partition",
2545
2546 /*
2547 * Create mid-lived context for aggregate trans values etc.
2548 *
2549 * Note that moving aggregates each use their own private context, not
2550 * this one.
2551 */
2552 winstate->aggcontext =
2554 "WindowAgg Aggregates",
2556
2557 /* Only the top-level WindowAgg may have a qual */
2558 Assert(node->plan.qual == NIL || node->topWindow);
2559
2560 /* Initialize the qual */
2561 winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2562 (PlanState *) winstate);
2563
2564 /*
2565 * Setup the run condition, if we received one from the query planner.
2566 * When set, this may allow us to move into pass-through mode so that we
2567 * don't have to perform any further evaluation of WindowFuncs in the
2568 * current partition or possibly stop returning tuples altogether when all
2569 * tuples are in the same partition.
2570 */
2571 winstate->runcondition = ExecInitQual(node->runCondition,
2572 (PlanState *) winstate);
2573
2574 /*
2575 * When we're not the top-level WindowAgg node or we are but have a
2576 * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2577 * modes when the runCondition becomes false.
2578 */
2579 winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2580
2581 /* remember if we're the top-window or we are below the top-window */
2582 winstate->top_window = node->topWindow;
2583
2584 /*
2585 * initialize child nodes
2586 */
2587 outerPlan = outerPlan(node);
2588 outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2589
2590 /*
2591 * initialize source tuple type (which is also the tuple type that we'll
2592 * store in the tuplestore and use in all our working slots).
2593 */
2596
2597 /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2598 winstate->ss.ps.outeropsset = true;
2599 winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2600 winstate->ss.ps.outeropsfixed = true;
2601
2602 /*
2603 * tuple table initialization
2604 */
2607 winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2609 winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2611 winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2613
2614 /*
2615 * create frame head and tail slots only if needed (must create slots in
2616 * exactly the same cases that update_frameheadpos and update_frametailpos
2617 * need them)
2618 */
2619 winstate->framehead_slot = winstate->frametail_slot = NULL;
2620
2621 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2622 {
2623 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2624 node->ordNumCols != 0) ||
2625 (frameOptions & FRAMEOPTION_START_OFFSET))
2628 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2629 node->ordNumCols != 0) ||
2630 (frameOptions & FRAMEOPTION_END_OFFSET))
2633 }
2634
2635 /*
2636 * Initialize result slot, type and projection.
2637 */
2639 ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2640
2641 /* Set up data for comparing tuples */
2642 if (node->partNumCols > 0)
2643 winstate->partEqfunction =
2645 node->partNumCols,
2646 node->partColIdx,
2647 node->partOperators,
2648 node->partCollations,
2649 &winstate->ss.ps);
2650
2651 if (node->ordNumCols > 0)
2652 winstate->ordEqfunction =
2654 node->ordNumCols,
2655 node->ordColIdx,
2656 node->ordOperators,
2657 node->ordCollations,
2658 &winstate->ss.ps);
2659
2660 /*
2661 * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2662 */
2663 numfuncs = winstate->numfuncs;
2664 numaggs = winstate->numaggs;
2665 econtext = winstate->ss.ps.ps_ExprContext;
2666 econtext->ecxt_aggvalues = palloc0_array(Datum, numfuncs);
2667 econtext->ecxt_aggnulls = palloc0_array(bool, numfuncs);
2668
2669 /*
2670 * allocate per-wfunc/per-agg state information.
2671 */
2672 perfunc = palloc0_array(WindowStatePerFuncData, numfuncs);
2673 peragg = palloc0_array(WindowStatePerAggData, numaggs);
2674 winstate->perfunc = perfunc;
2675 winstate->peragg = peragg;
2676
2677 wfuncno = -1;
2678 aggno = -1;
2679 foreach(l, winstate->funcs)
2680 {
2681 WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2682 WindowFunc *wfunc = wfuncstate->wfunc;
2685 int i;
2686
2687 if (wfunc->winref != node->winref) /* planner screwed up? */
2688 elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2689 wfunc->winref, node->winref);
2690
2691 /*
2692 * Look for a previous duplicate window function, which needs the same
2693 * ignore_nulls value
2694 */
2695 for (i = 0; i <= wfuncno; i++)
2696 {
2697 if (equal(wfunc, perfunc[i].wfunc) &&
2698 !contain_volatile_functions((Node *) wfunc))
2699 break;
2700 }
2701 if (i <= wfuncno && wfunc->ignore_nulls == perfunc[i].ignore_nulls)
2702 {
2703 /* Found a match to an existing entry, so just mark it */
2704 wfuncstate->wfuncno = i;
2705 continue;
2706 }
2707
2708 /* Nope, so assign a new PerAgg record */
2709 perfuncstate = &perfunc[++wfuncno];
2710
2711 /* Mark WindowFunc state node with assigned index in the result array */
2712 wfuncstate->wfuncno = wfuncno;
2713
2714 /* Check permission to call window function */
2716 ACL_EXECUTE);
2717 if (aclresult != ACLCHECK_OK)
2719 get_func_name(wfunc->winfnoid));
2720 InvokeFunctionExecuteHook(wfunc->winfnoid);
2721
2722 /* Fill in the perfuncstate data */
2723 perfuncstate->wfuncstate = wfuncstate;
2724 perfuncstate->wfunc = wfunc;
2725 perfuncstate->numArguments = list_length(wfuncstate->args);
2726 perfuncstate->winCollation = wfunc->inputcollid;
2727
2728 get_typlenbyval(wfunc->wintype,
2729 &perfuncstate->resulttypeLen,
2730 &perfuncstate->resulttypeByVal);
2731
2732 /*
2733 * If it's really just a plain aggregate function, we'll emulate the
2734 * Agg environment for it.
2735 */
2736 perfuncstate->plain_agg = wfunc->winagg;
2737 if (wfunc->winagg)
2738 {
2740
2741 perfuncstate->aggno = ++aggno;
2742 peraggstate = &winstate->peragg[aggno];
2743 initialize_peragg(winstate, wfunc, peraggstate);
2744 peraggstate->wfuncno = wfuncno;
2745 }
2746 else
2747 {
2749
2750 winobj->winstate = winstate;
2751 winobj->argstates = wfuncstate->args;
2752 winobj->localmem = NULL;
2753 perfuncstate->winobj = winobj;
2754 winobj->ignore_nulls = wfunc->ignore_nulls;
2756
2757 /* It's a real window function, so set up to call it. */
2758 fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2759 econtext->ecxt_per_query_memory);
2760 fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2761 }
2762 }
2763
2764 /* Update numfuncs, numaggs to match number of unique functions found */
2765 winstate->numfuncs = wfuncno + 1;
2766 winstate->numaggs = aggno + 1;
2767
2768 /* Set up WindowObject for aggregates, if needed */
2769 if (winstate->numaggs > 0)
2770 {
2772
2773 agg_winobj->winstate = winstate;
2774 agg_winobj->argstates = NIL;
2775 agg_winobj->localmem = NULL;
2776 /* make sure markptr = -1 to invalidate. It may not get used */
2777 agg_winobj->markptr = -1;
2778 agg_winobj->readptr = -1;
2779 winstate->agg_winobj = agg_winobj;
2780 }
2781
2782 /* Set the status to running */
2783 winstate->status = WINDOWAGG_RUN;
2784
2785 /* initialize frame bound offset expressions */
2786 winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2787 (PlanState *) winstate);
2788 winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2789 (PlanState *) winstate);
2790
2791 /* Lookup in_range support functions if needed */
2792 if (OidIsValid(node->startInRangeFunc))
2793 fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2794 if (OidIsValid(node->endInRangeFunc))
2795 fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2796 winstate->inRangeColl = node->inRangeColl;
2797 winstate->inRangeAsc = node->inRangeAsc;
2798 winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2799
2800 winstate->all_first = true;
2801 winstate->partition_spooled = false;
2802 winstate->more_partitions = false;
2803 winstate->next_partition = true;
2804
2805 return winstate;
2806}
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:3879
bool contain_volatile_functions(Node *clause)
Definition clauses.c:549
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition execExpr.c:250
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
const TupleTableSlotOps TTSOpsVirtual
Definition execTuples.c:84
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
const TupleTableSlotOps TTSOpsMinimalTuple
Definition execTuples.c:86
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition execUtils.c:709
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition execUtils.c:490
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition execUtils.c:588
@ WINDOWAGG_RUN
Definition execnodes.h:2493
#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:77
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:1828
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
Oid GetUserId(void)
Definition miscinit.c:470
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
static void init_notnull_info(WindowObject winobj, WindowStatePerFunc perfuncstate)
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
#define makeNode(_type_)
Definition nodes.h:161
#define InvokeFunctionExecuteHook(objectId)
#define FRAMEOPTION_START_CURRENT_ROW
Definition parsenodes.h:618
@ OBJECT_FUNCTION
#define FRAMEOPTION_RANGE
Definition parsenodes.h:610
#define ACL_EXECUTE
Definition parsenodes.h:83
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
MemoryContext ecxt_per_query_memory
Definition execnodes.h:291
bool outeropsset
Definition execnodes.h:1260
const TupleTableSlotOps * outerops
Definition execnodes.h:1252
ExprState * qual
Definition execnodes.h:1198
bool outeropsfixed
Definition execnodes.h:1256
EState * state
Definition execnodes.h:1179
ExecProcNodeMtd ExecProcNode
Definition execnodes.h:1183
List * qual
Definition plannodes.h:235
TupleTableSlot * ss_ScanTupleSlot
Definition execnodes.h:1636
TupleDesc tts_tupleDescriptor
Definition tuptable.h:129
FmgrInfo endInRangeFunc
Definition execnodes.h:2535
FmgrInfo startInRangeFunc
Definition execnodes.h:2534
ExprState * runcondition
Definition execnodes.h:2546
TupleTableSlot * temp_slot_2
Definition execnodes.h:2584
WindowAggStatus status
Definition execnodes.h:2525
bool inRangeNullsFirst
Definition execnodes.h:2538
ExprState * partEqfunction
Definition execnodes.h:2510
bool use_pass_through
Definition execnodes.h:2541
int partNumCols
Definition plannodes.h:1258
Oid endInRangeFunc
Definition plannodes.h:1302
Node * endOffset
Definition plannodes.h:1288
bool topWindow
Definition plannodes.h:1317
Oid inRangeColl
Definition plannodes.h:1305
Node * startOffset
Definition plannodes.h:1285
List * runCondition
Definition plannodes.h:1291
Oid startInRangeFunc
Definition plannodes.h:1299
bool inRangeAsc
Definition plannodes.h:1308
Index winref
Definition plannodes.h:1255
bool inRangeNullsFirst
Definition plannodes.h:1311
int ordNumCols
Definition plannodes.h:1270
int frameOptions
Definition plannodes.h:1282
WindowFunc * wfunc
Definition execnodes.h:935
Index winref
Definition primnodes.h:612
WindowAggState * winstate

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

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2848 of file nodeWindowAgg.c.

2849{
2851 ExprContext *econtext = node->ss.ps.ps_ExprContext;
2852
2853 node->status = WINDOWAGG_RUN;
2854 node->all_first = true;
2855
2856 /* release tuplestore et al */
2857 release_partition(node);
2858
2859 /* release all temp tuples, but especially first_part_slot */
2865 if (node->framehead_slot)
2867 if (node->frametail_slot)
2869
2870 /* Forget current wfunc values */
2871 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2872 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2873
2874 /*
2875 * if chgParam of subnode is not null then plan will be re-scanned by
2876 * first ExecProcNode.
2877 */
2878 if (outerPlan->chgParam == NULL)
2880}
#define MemSet(start, val, len)
Definition c.h:1109
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 2232 of file nodeWindowAgg.c.

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

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

3529{
3530 uint8 *mbp;
3531 uint8 mb;
3532 int64 bpos;
3533
3534 grow_notnull_info(winobj, pos, argno);
3535 bpos = NN_POS_TO_BYTES(pos);
3536 mbp = winobj->notnull_info[argno];
3537 mb = mbp[bpos];
3538 return (mb >> (NN_SHIFT(pos))) & NN_MASK;
3539}
uint8_t uint8
Definition c.h:616
static void grow_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_MASK
#define NN_SHIFT(pos)

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

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3161 of file nodeWindowAgg.c.

3162{
3163 Oid typinput,
3164 typioparam;
3165 char *strInitVal;
3166 Datum initVal;
3167
3168 getTypeInputInfo(transtype, &typinput, &typioparam);
3171 typioparam, -1);
3173 return initVal;
3174}
#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:3096
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 3296 of file nodeWindowAgg.c.

3298{
3299 WindowAggState *winstate;
3300 ExprContext *econtext;
3301 TupleTableSlot *slot;
3302
3303 winstate = winobj->winstate;
3304 slot = winstate->temp_slot_1;
3305 if (!window_gettupleslot(winobj, abs_pos, slot))
3306 {
3307 /* out of partition */
3308 if (isout)
3309 *isout = true;
3310 *isnull = true;
3311 return (Datum) 0;
3312 }
3313
3314 if (isout)
3315 *isout = false;
3316 econtext = winstate->ss.ps.ps_ExprContext;
3317 econtext->ecxt_outertuple = slot;
3319 (winobj->argstates, argno),
3320 econtext, isnull);
3321}
static void * list_nth(const List *list, int n)
Definition pg_list.h:299

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

3485{
3486/* initial number of notnull info members */
3487#define INIT_NOT_NULL_INFO_NUM 128
3488
3489 if (pos >= winobj->num_notnull_info[argno])
3490 {
3491 /* We may be called in a short-lived context */
3494
3495 for (;;)
3496 {
3498 (winobj->num_notnull_info[argno]);
3499 Size newsize;
3500
3501 if (oldsize == 0) /* memory has not been allocated yet for this
3502 * arg */
3503 {
3505 winobj->notnull_info[argno] = palloc0(newsize);
3506 }
3507 else
3508 {
3509 newsize = oldsize * 2;
3510 winobj->notnull_info[argno] =
3512 }
3514 if (winobj->num_notnull_info[argno] > pos)
3515 break;
3516 }
3517 MemoryContextSwitchTo(oldcontext);
3518 }
3519}
size_t Size
Definition c.h:691
void * repalloc0(void *pointer, Size oldsize, Size size)
Definition mcxt.c:1704
void * palloc0(Size size)
Definition mcxt.c:1417
#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 3329 of file nodeWindowAgg.c.

3332{
3333 WindowAggState *winstate;
3334 ExprContext *econtext;
3335 TupleTableSlot *slot;
3336 Datum datum;
3337 int64 abs_pos;
3339 int notnull_offset;
3340 int notnull_relpos;
3341 int forward;
3342
3343 Assert(WindowObjectIsValid(winobj));
3344 winstate = winobj->winstate;
3345 econtext = winstate->ss.ps.ps_ExprContext;
3346 slot = winstate->temp_slot_1;
3347 datum = (Datum) 0;
3348 notnull_offset = 0;
3350
3351 switch (seektype)
3352 {
3354 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3355 abs_pos = mark_pos = 0; /* keep compiler quiet */
3356 break;
3357 case WINDOW_SEEK_HEAD:
3358 /* rejecting relpos < 0 is easy and simplifies code below */
3359 if (relpos < 0)
3360 goto out_of_frame;
3361 update_frameheadpos(winstate);
3362 abs_pos = winstate->frameheadpos;
3363 mark_pos = winstate->frameheadpos;
3364 forward = 1;
3365 break;
3366 case WINDOW_SEEK_TAIL:
3367 /* rejecting relpos > 0 is easy and simplifies code below */
3368 if (relpos > 0)
3369 goto out_of_frame;
3370 update_frametailpos(winstate);
3371 abs_pos = winstate->frametailpos - 1;
3372 mark_pos = 0; /* keep compiler quiet */
3373 forward = -1;
3374 break;
3375 default:
3376 elog(ERROR, "unrecognized window seek type: %d", seektype);
3377 abs_pos = mark_pos = 0; /* keep compiler quiet */
3378 break;
3379 }
3380
3381 /*
3382 * Get the next nonnull value in the frame, moving forward or backward
3383 * until we find a value or reach the frame's end.
3384 */
3385 do
3386 {
3387 int inframe;
3388 int v;
3389
3390 /*
3391 * Check apparent out of frame case. We need to do this because we
3392 * may not call window_gettupleslot before row_is_in_frame, which
3393 * supposes abs_pos is never negative.
3394 */
3395 if (abs_pos < 0)
3396 goto out_of_frame;
3397
3398 /* check whether row is in frame */
3399 inframe = row_is_in_frame(winobj, abs_pos, slot, true);
3400 if (inframe == -1)
3401 goto out_of_frame;
3402 else if (inframe == 0)
3403 goto advance;
3404
3405 if (isout)
3406 *isout = false;
3407
3408 v = get_notnull_info(winobj, abs_pos, argno);
3409 if (v == NN_NULL) /* this row is known to be NULL */
3410 goto advance;
3411
3412 else if (v == NN_UNKNOWN) /* need to check NULL or not */
3413 {
3414 if (!window_gettupleslot(winobj, abs_pos, slot))
3415 goto out_of_frame;
3416
3417 econtext->ecxt_outertuple = slot;
3418 datum = ExecEvalExpr(
3419 (ExprState *) list_nth(winobj->argstates,
3420 argno), econtext,
3421 isnull);
3422 if (!*isnull)
3424
3425 /* record the row status */
3426 put_notnull_info(winobj, abs_pos, argno, *isnull);
3427 }
3428 else /* this row is known to be NOT NULL */
3429 {
3432 {
3433 /* to prepare exiting this loop, datum needs to be set */
3434 if (!window_gettupleslot(winobj, abs_pos, slot))
3435 goto out_of_frame;
3436
3437 econtext->ecxt_outertuple = slot;
3438 datum = ExecEvalExpr(
3440 (winobj->argstates, argno),
3441 econtext, isnull);
3442 }
3443 }
3444advance:
3445 abs_pos += forward;
3446 } while (notnull_offset <= notnull_relpos);
3447
3448 if (set_mark)
3450
3451 return datum;
3452
3454 if (isout)
3455 *isout = true;
3456 *isnull = true;
3457 return (Datum) 0;
3458}
#define NN_UNKNOWN
static void put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull)
#define NN_NULL
static uint8 get_notnull_info(WindowObject winobj, int64 pos, int argno)
#define WINDOW_SEEK_TAIL
Definition windowapi.h:36
#define WINDOW_SEEK_HEAD
Definition windowapi.h:35
#define WindowObjectIsValid(winobj)
Definition windowapi.h:43
#define WINDOW_SEEK_CURRENT
Definition windowapi.h:34

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

3467{
3468 int numargs = perfuncstate->numArguments;
3469
3470 if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3471 {
3472 winobj->notnull_info = palloc0_array(uint8 *, numargs);
3473 winobj->num_notnull_info = palloc0_array(int64, numargs);
3474 }
3475}

References fb(), WindowObjectData::ignore_nulls, WindowObjectData::notnull_info, WindowObjectData::num_notnull_info, palloc0_array, and PARSER_IGNORE_NULLS.

Referenced by ExecInitWindowAgg().

◆ initialize_peragg()

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

Definition at line 2888 of file nodeWindowAgg.c.

2890{
2892 int numArguments;
2895 Oid aggtranstype;
2898 bool use_ma_code;
2899 Oid transfn_oid,
2900 invtransfn_oid,
2901 finalfn_oid;
2902 bool finalextra;
2903 char finalmodify;
2906 *finalfnexpr;
2908 int i;
2909 ListCell *lc;
2910
2911 numArguments = list_length(wfunc->args);
2912
2913 i = 0;
2914 foreach(lc, wfunc->args)
2915 {
2916 inputTypes[i++] = exprType((Node *) lfirst(lc));
2917 }
2918
2921 elog(ERROR, "cache lookup failed for aggregate %u",
2922 wfunc->winfnoid);
2924
2925 /*
2926 * Figure out whether we want to use the moving-aggregate implementation,
2927 * and collect the right set of fields from the pg_aggregate entry.
2928 *
2929 * It's possible that an aggregate would supply a safe moving-aggregate
2930 * implementation and an unsafe normal one, in which case our hand is
2931 * forced. Otherwise, if the frame head can't move, we don't need
2932 * moving-aggregate code. Even if we'd like to use it, don't do so if the
2933 * aggregate's arguments (and FILTER clause if any) contain any calls to
2934 * volatile functions. Otherwise, the difference between restarting and
2935 * not restarting the aggregation would be user-visible.
2936 *
2937 * We also don't risk using moving aggregates when there are subplans in
2938 * the arguments or FILTER clause. This is partly because
2939 * contain_volatile_functions() doesn't look inside subplans; but there
2940 * are other reasons why a subplan's output might be volatile. For
2941 * example, syncscan mode can render the results nonrepeatable.
2942 */
2943 if (!OidIsValid(aggform->aggminvtransfn))
2944 use_ma_code = false; /* sine qua non */
2945 else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2946 aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2947 use_ma_code = true; /* decision forced by safety */
2949 use_ma_code = false; /* non-moving frame head */
2950 else if (contain_volatile_functions((Node *) wfunc))
2951 use_ma_code = false; /* avoid possible behavioral change */
2952 else if (contain_subplans((Node *) wfunc))
2953 use_ma_code = false; /* subplans might contain volatile functions */
2954 else
2955 use_ma_code = true; /* yes, let's use it */
2956 if (use_ma_code)
2957 {
2958 peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2959 peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2960 peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2961 finalextra = aggform->aggmfinalextra;
2962 finalmodify = aggform->aggmfinalmodify;
2963 aggtranstype = aggform->aggmtranstype;
2965 }
2966 else
2967 {
2968 peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2969 peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2970 peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2971 finalextra = aggform->aggfinalextra;
2972 finalmodify = aggform->aggfinalmodify;
2973 aggtranstype = aggform->aggtranstype;
2975 }
2976
2977 /*
2978 * ExecInitWindowAgg already checked permission to call aggregate function
2979 * ... but we still need to check the component functions
2980 */
2981
2982 /* Check that aggregate owner has permission to call component fns */
2983 {
2985 Oid aggOwner;
2986
2988 ObjectIdGetDatum(wfunc->winfnoid));
2990 elog(ERROR, "cache lookup failed for function %u",
2991 wfunc->winfnoid);
2994
2996 ACL_EXECUTE);
2997 if (aclresult != ACLCHECK_OK)
2999 get_func_name(transfn_oid));
3000 InvokeFunctionExecuteHook(transfn_oid);
3001
3002 if (OidIsValid(invtransfn_oid))
3003 {
3005 ACL_EXECUTE);
3006 if (aclresult != ACLCHECK_OK)
3008 get_func_name(invtransfn_oid));
3009 InvokeFunctionExecuteHook(invtransfn_oid);
3010 }
3011
3012 if (OidIsValid(finalfn_oid))
3013 {
3015 ACL_EXECUTE);
3016 if (aclresult != ACLCHECK_OK)
3018 get_func_name(finalfn_oid));
3019 InvokeFunctionExecuteHook(finalfn_oid);
3020 }
3021 }
3022
3023 /*
3024 * If the selected finalfn isn't read-only, we can't run this aggregate as
3025 * a window function. This is a user-facing error, so we take a bit more
3026 * care with the error message than elsewhere in this function.
3027 */
3029 ereport(ERROR,
3031 errmsg("aggregate function %s does not support use as a window function",
3032 format_procedure(wfunc->winfnoid))));
3033
3034 /* Detect how many arguments to pass to the finalfn */
3035 if (finalextra)
3036 peraggstate->numFinalArgs = numArguments + 1;
3037 else
3038 peraggstate->numFinalArgs = 1;
3039
3040 /* resolve actual type of transition state, if polymorphic */
3041 aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
3042 aggtranstype,
3043 inputTypes,
3044 numArguments);
3045
3046 /* build expression trees using actual argument & result types */
3048 numArguments,
3049 0, /* no ordered-set window functions yet */
3050 false, /* no variadic window functions yet */
3051 aggtranstype,
3052 wfunc->inputcollid,
3053 transfn_oid,
3054 invtransfn_oid,
3055 &transfnexpr,
3057
3058 /* set up infrastructure for calling the transfn(s) and finalfn */
3059 fmgr_info(transfn_oid, &peraggstate->transfn);
3061
3062 if (OidIsValid(invtransfn_oid))
3063 {
3064 fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
3066 }
3067
3068 if (OidIsValid(finalfn_oid))
3069 {
3071 peraggstate->numFinalArgs,
3072 aggtranstype,
3073 wfunc->wintype,
3074 wfunc->inputcollid,
3075 finalfn_oid,
3076 &finalfnexpr);
3077 fmgr_info(finalfn_oid, &peraggstate->finalfn);
3079 }
3080
3081 /* get info about relevant datatypes */
3082 get_typlenbyval(wfunc->wintype,
3083 &peraggstate->resulttypeLen,
3084 &peraggstate->resulttypeByVal);
3085 get_typlenbyval(aggtranstype,
3086 &peraggstate->transtypeLen,
3087 &peraggstate->transtypeByVal);
3088
3089 /*
3090 * initval is potentially null, so don't try to access it as a struct
3091 * field. Must do it the hard way with SysCacheGetAttr.
3092 */
3094 &peraggstate->initValueIsNull);
3095
3096 if (peraggstate->initValueIsNull)
3097 peraggstate->initValue = (Datum) 0;
3098 else
3100 aggtranstype);
3101
3102 /*
3103 * If the transfn is strict and the initval is NULL, make sure input type
3104 * and transtype are the same (or at least binary-compatible), so that
3105 * it's OK to use the first input value as the initial transValue. This
3106 * should have been checked at agg definition time, but we must check
3107 * again in case the transfn's strictness property has been changed.
3108 */
3109 if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
3110 {
3111 if (numArguments < 1 ||
3112 !IsBinaryCoercible(inputTypes[0], aggtranstype))
3113 ereport(ERROR,
3115 errmsg("aggregate %u needs to have compatible input type and transition type",
3116 wfunc->winfnoid)));
3117 }
3118
3119 /*
3120 * Insist that forward and inverse transition functions have the same
3121 * strictness setting. Allowing them to differ would require handling
3122 * more special cases in advance_windowaggregate and
3123 * advance_windowaggregate_base, for no discernible benefit. This should
3124 * have been checked at agg definition time, but we must check again in
3125 * case either function's strictness property has been changed.
3126 */
3127 if (OidIsValid(invtransfn_oid) &&
3128 peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
3129 ereport(ERROR,
3131 errmsg("strictness of aggregate's forward and inverse transition functions must match")));
3132
3133 /*
3134 * Moving aggregates use their own aggcontext.
3135 *
3136 * This is necessary because they might restart at different times, so we
3137 * might never be able to reset the shared context otherwise. We can't
3138 * make it the aggregates' responsibility to clean up after themselves,
3139 * because strict aggregates must be restarted whenever we remove their
3140 * last non-NULL input, which the aggregate won't be aware is happening.
3141 * Also, just pfree()ing the transValue upon restarting wouldn't help,
3142 * since we'd miss any indirectly referenced data. We could, in theory,
3143 * make the memory allocation rules for moving aggregates different than
3144 * they have historically been for plain aggregates, but that seems grotty
3145 * and likely to lead to memory leaks.
3146 */
3147 if (OidIsValid(invtransfn_oid))
3148 peraggstate->aggcontext =
3150 "WindowAgg Per Aggregate",
3152 else
3153 peraggstate->aggcontext = winstate->aggcontext;
3154
3156
3157 return peraggstate;
3158}
int16 AttrNumber
Definition attnum.h:21
bool contain_subplans(Node *clause)
Definition clauses.c:341
#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:2349
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition parse_agg.c:2138
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:2241
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition parsenodes.h:614
END_CATALOG_STRUCT typedef FormData_pg_aggregate * Form_pg_aggregate
END_CATALOG_STRUCT typedef FormData_pg_proc * Form_pg_proc
Definition pg_proc.h:140
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
#define InvalidOid
char * format_procedure(Oid procedure_oid)
Definition regproc.c:305
List * args
Definition primnodes.h:606
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595

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

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

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

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

3550{
3551 uint8 *mbp;
3552 uint8 mb;
3553 int64 bpos;
3554 uint8 val = isnull ? NN_NULL : NN_NOTNULL;
3555 int shift;
3556
3557 grow_notnull_info(winobj, pos, argno);
3558 bpos = NN_POS_TO_BYTES(pos);
3559 mbp = winobj->notnull_info[argno];
3560 mb = mbp[bpos];
3561 shift = NN_SHIFT(pos);
3562 mb &= ~(NN_MASK << shift); /* clear map */
3563 mb |= (val << shift); /* update map */
3564 mbp[bpos] = mb;
3565}
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, and val.

Referenced by ignorenulls_getfuncarginframe(), and WinGetFuncArgInPartition().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1438 of file nodeWindowAgg.c.

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

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

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

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

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

1846{
1847 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1848 int frameOptions = winstate->frameOptions;
1849 MemoryContext oldcontext;
1850
1851 if (winstate->frametail_valid)
1852 return; /* already known for current row */
1853
1854 /* We may be called in a short-lived context */
1856
1857 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1858 {
1859 /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1860 spool_tuples(winstate, -1);
1861 winstate->frametailpos = winstate->spooled_rows;
1862 winstate->frametail_valid = true;
1863 }
1864 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1865 {
1866 if (frameOptions & FRAMEOPTION_ROWS)
1867 {
1868 /* In ROWS mode, exactly the rows up to current are in frame */
1869 winstate->frametailpos = winstate->currentpos + 1;
1870 winstate->frametail_valid = true;
1871 }
1872 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1873 {
1874 /* If no ORDER BY, all rows are peers with each other */
1875 if (node->ordNumCols == 0)
1876 {
1877 spool_tuples(winstate, -1);
1878 winstate->frametailpos = winstate->spooled_rows;
1879 winstate->frametail_valid = true;
1880 MemoryContextSwitchTo(oldcontext);
1881 return;
1882 }
1883
1884 /*
1885 * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1886 * row that is a peer of current row, frame tail is the row after
1887 * that (if any). We keep a copy of the last-known frame tail row
1888 * in frametail_slot, and advance as necessary. Note that if we
1889 * reach end of partition, we will leave frametailpos = end+1 and
1890 * frametail_slot empty.
1891 */
1893 winstate->frametail_ptr);
1894 if (winstate->frametailpos == 0 &&
1895 TupIsNull(winstate->frametail_slot))
1896 {
1897 /* fetch first row into frametail_slot, if we didn't already */
1898 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1899 winstate->frametail_slot))
1900 elog(ERROR, "unexpected end of tuplestore");
1901 }
1902
1903 while (!TupIsNull(winstate->frametail_slot))
1904 {
1905 if (winstate->frametailpos > winstate->currentpos &&
1906 !are_peers(winstate, winstate->frametail_slot,
1907 winstate->ss.ss_ScanTupleSlot))
1908 break; /* this row is the frame tail */
1909 /* Note we advance frametailpos even if the fetch fails */
1910 winstate->frametailpos++;
1911 spool_tuples(winstate, winstate->frametailpos);
1912 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1913 winstate->frametail_slot))
1914 break; /* end of partition */
1915 }
1916 winstate->frametail_valid = true;
1917 }
1918 else
1919 Assert(false);
1920 }
1921 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1922 {
1923 if (frameOptions & FRAMEOPTION_ROWS)
1924 {
1925 /* In ROWS mode, bound is physically n before/after current */
1926 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1927
1928 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1929 offset = -offset;
1930
1931 winstate->frametailpos = winstate->currentpos + offset + 1;
1932 /* smallest allowable value of frametailpos is 0 */
1933 if (winstate->frametailpos < 0)
1934 winstate->frametailpos = 0;
1935 else if (winstate->frametailpos > winstate->currentpos + 1)
1936 {
1937 /* make sure frametailpos is not past end of partition */
1938 spool_tuples(winstate, winstate->frametailpos - 1);
1939 if (winstate->frametailpos > winstate->spooled_rows)
1940 winstate->frametailpos = winstate->spooled_rows;
1941 }
1942 winstate->frametail_valid = true;
1943 }
1944 else if (frameOptions & FRAMEOPTION_RANGE)
1945 {
1946 /*
1947 * In RANGE END_OFFSET mode, frame end is the last row that
1948 * satisfies the in_range constraint relative to the current row,
1949 * frame tail is the row after that (if any). We keep a copy of
1950 * the last-known frame tail row in frametail_slot, and advance as
1951 * necessary. Note that if we reach end of partition, we will
1952 * leave frametailpos = end+1 and frametail_slot empty.
1953 */
1954 int sortCol = node->ordColIdx[0];
1955 bool sub,
1956 less;
1957
1958 /* We must have an ordering column */
1959 Assert(node->ordNumCols == 1);
1960
1961 /* Precompute flags for in_range checks */
1962 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1963 sub = true; /* subtract endOffset from current row */
1964 else
1965 sub = false; /* add it */
1966 less = true; /* normally, we want frame tail <= sum */
1967 /* If sort order is descending, flip both flags */
1968 if (!winstate->inRangeAsc)
1969 {
1970 sub = !sub;
1971 less = false;
1972 }
1973
1975 winstate->frametail_ptr);
1976 if (winstate->frametailpos == 0 &&
1977 TupIsNull(winstate->frametail_slot))
1978 {
1979 /* fetch first row into frametail_slot, if we didn't already */
1980 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1981 winstate->frametail_slot))
1982 elog(ERROR, "unexpected end of tuplestore");
1983 }
1984
1985 while (!TupIsNull(winstate->frametail_slot))
1986 {
1987 Datum tailval,
1988 currval;
1989 bool tailisnull,
1990 currisnull;
1991
1993 &tailisnull);
1995 &currisnull);
1996 if (tailisnull || currisnull)
1997 {
1998 /* order of the rows depends only on nulls_first */
1999 if (winstate->inRangeNullsFirst)
2000 {
2001 /* advance tail if tail is null or curr is not */
2002 if (!tailisnull)
2003 break;
2004 }
2005 else
2006 {
2007 /* advance tail if tail is not null or curr is null */
2008 if (!currisnull)
2009 break;
2010 }
2011 }
2012 else
2013 {
2015 winstate->inRangeColl,
2016 tailval,
2017 currval,
2018 winstate->endOffsetValue,
2019 BoolGetDatum(sub),
2020 BoolGetDatum(less))))
2021 break; /* this row is the correct frame tail */
2022 }
2023 /* Note we advance frametailpos even if the fetch fails */
2024 winstate->frametailpos++;
2025 spool_tuples(winstate, winstate->frametailpos);
2026 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2027 winstate->frametail_slot))
2028 break; /* end of partition */
2029 }
2030 winstate->frametail_valid = true;
2031 }
2032 else if (frameOptions & FRAMEOPTION_GROUPS)
2033 {
2034 /*
2035 * In GROUPS END_OFFSET mode, frame end is the last row of the
2036 * last peer group whose number satisfies the offset constraint,
2037 * and frame tail is the row after that (if any). We keep a copy
2038 * of the last-known frame tail row in frametail_slot, and advance
2039 * as necessary. Note that if we reach end of partition, we will
2040 * leave frametailpos = end+1 and frametail_slot empty.
2041 */
2042 int64 offset = DatumGetInt64(winstate->endOffsetValue);
2044
2045 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
2046 maxtailgroup = winstate->currentgroup - offset;
2047 else
2048 maxtailgroup = winstate->currentgroup + offset;
2049
2051 winstate->frametail_ptr);
2052 if (winstate->frametailpos == 0 &&
2053 TupIsNull(winstate->frametail_slot))
2054 {
2055 /* fetch first row into frametail_slot, if we didn't already */
2056 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2057 winstate->frametail_slot))
2058 elog(ERROR, "unexpected end of tuplestore");
2059 }
2060
2061 while (!TupIsNull(winstate->frametail_slot))
2062 {
2063 if (winstate->frametailgroup > maxtailgroup)
2064 break; /* this row is the correct frame tail */
2065 ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
2066 /* Note we advance frametailpos even if the fetch fails */
2067 winstate->frametailpos++;
2068 spool_tuples(winstate, winstate->frametailpos);
2069 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2070 winstate->frametail_slot))
2071 break; /* end of partition */
2072 if (!are_peers(winstate, winstate->temp_slot_2,
2073 winstate->frametail_slot))
2074 winstate->frametailgroup++;
2075 }
2076 ExecClearTuple(winstate->temp_slot_2);
2077 winstate->frametail_valid = true;
2078 }
2079 else
2080 Assert(false);
2081 }
2082 else
2083 Assert(false);
2084
2085 MemoryContextSwitchTo(oldcontext);
2086}

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

2096{
2097 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2098 MemoryContext oldcontext;
2099
2100 if (winstate->grouptail_valid)
2101 return; /* already known for current row */
2102
2103 /* We may be called in a short-lived context */
2105
2106 /* If no ORDER BY, all rows are peers with each other */
2107 if (node->ordNumCols == 0)
2108 {
2109 spool_tuples(winstate, -1);
2110 winstate->grouptailpos = winstate->spooled_rows;
2111 winstate->grouptail_valid = true;
2112 MemoryContextSwitchTo(oldcontext);
2113 return;
2114 }
2115
2116 /*
2117 * Because grouptail_valid is reset only when current row advances into a
2118 * new peer group, we always reach here knowing that grouptailpos needs to
2119 * be advanced by at least one row. Hence, unlike the otherwise similar
2120 * case for frame tail tracking, we do not need persistent storage of the
2121 * group tail row.
2122 */
2123 Assert(winstate->grouptailpos <= winstate->currentpos);
2125 winstate->grouptail_ptr);
2126 for (;;)
2127 {
2128 /* Note we advance grouptailpos even if the fetch fails */
2129 winstate->grouptailpos++;
2130 spool_tuples(winstate, winstate->grouptailpos);
2131 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2132 winstate->temp_slot_2))
2133 break; /* end of partition */
2134 if (winstate->grouptailpos > winstate->currentpos &&
2135 !are_peers(winstate, winstate->temp_slot_2,
2136 winstate->ss.ss_ScanTupleSlot))
2137 break; /* this row is the group tail */
2138 }
2139 ExecClearTuple(winstate->temp_slot_2);
2140 winstate->grouptail_valid = true;
2141
2142 MemoryContextSwitchTo(oldcontext);
2143}

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

3583{
3584 Assert(WindowObjectIsValid(winobj));
3586 {
3587 const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
3588
3589 if (!funcname)
3590 elog(ERROR, "could not get function name");
3591 ereport(ERROR,
3593 errmsg("function %s does not allow RESPECT/IGNORE NULLS",
3594 funcname)));
3595 }
3596 else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3597 winobj->ignore_nulls = IGNORE_NULLS;
3598}
#define funcname
#define NO_NULLTREATMENT
Definition primnodes.h:589
Oid fn_oid
Definition fmgr.h:59
FmgrInfo * flinfo
Definition fmgr.h:87

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

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

◆ window_gettupleslot()

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

Definition at line 3206 of file nodeWindowAgg.c.

3207{
3208 WindowAggState *winstate = winobj->winstate;
3209 MemoryContext oldcontext;
3210
3211 /* often called repeatedly in a row */
3213
3214 /* Don't allow passing -1 to spool_tuples here */
3215 if (pos < 0)
3216 return false;
3217
3218 /* If necessary, fetch the tuple into the spool */
3219 spool_tuples(winstate, pos);
3220
3221 if (pos >= winstate->spooled_rows)
3222 return false;
3223
3224 if (pos < winobj->markpos)
3225 elog(ERROR, "cannot fetch row before WindowObject's mark position");
3226
3228
3229 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3230
3231 /*
3232 * Advance or rewind until we are within one tuple of the one we want.
3233 */
3234 if (winobj->seekpos < pos - 1)
3235 {
3236 if (!tuplestore_skiptuples(winstate->buffer,
3237 pos - 1 - winobj->seekpos,
3238 true))
3239 elog(ERROR, "unexpected end of tuplestore");
3240 winobj->seekpos = pos - 1;
3241 }
3242 else if (winobj->seekpos > pos + 1)
3243 {
3244 if (!tuplestore_skiptuples(winstate->buffer,
3245 winobj->seekpos - (pos + 1),
3246 false))
3247 elog(ERROR, "unexpected end of tuplestore");
3248 winobj->seekpos = pos + 1;
3249 }
3250 else if (winobj->seekpos == pos)
3251 {
3252 /*
3253 * There's no API to refetch the tuple at the current position. We
3254 * have to move one tuple forward, and then one backward. (We don't
3255 * do it the other way because we might try to fetch the row before
3256 * our mark, which isn't allowed.) XXX this case could stand to be
3257 * optimized.
3258 */
3259 tuplestore_advance(winstate->buffer, true);
3260 winobj->seekpos++;
3261 }
3262
3263 /*
3264 * Now we should be on the tuple immediately before or after the one we
3265 * want, so just fetch forwards or backwards as appropriate.
3266 *
3267 * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3268 * the fetched tuple. This ensures that the slot's contents remain valid
3269 * through manipulations of the tuplestore, which some callers depend on.
3270 */
3271 if (winobj->seekpos > pos)
3272 {
3273 if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3274 elog(ERROR, "unexpected end of tuplestore");
3275 winobj->seekpos--;
3276 }
3277 else
3278 {
3279 if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3280 elog(ERROR, "unexpected end of tuplestore");
3281 winobj->seekpos++;
3282 }
3283
3284 Assert(winobj->seekpos == pos);
3285
3286 MemoryContextSwitchTo(oldcontext);
3287
3288 return true;
3289}
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 4119 of file nodeWindowAgg.c.

4120{
4121 WindowAggState *winstate;
4122 ExprContext *econtext;
4123
4124 Assert(WindowObjectIsValid(winobj));
4125 winstate = winobj->winstate;
4126
4127 econtext = winstate->ss.ps.ps_ExprContext;
4128
4129 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
4130 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4131 econtext, isnull);
4132}

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

3923{
3924 WindowAggState *winstate;
3925 ExprContext *econtext;
3926 TupleTableSlot *slot;
3927 int64 abs_pos;
3929
3930 Assert(WindowObjectIsValid(winobj));
3931 winstate = winobj->winstate;
3932 econtext = winstate->ss.ps.ps_ExprContext;
3933 slot = winstate->temp_slot_1;
3934
3935 if (winobj->ignore_nulls == IGNORE_NULLS)
3937 set_mark, isnull, isout);
3938
3939 switch (seektype)
3940 {
3942 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3943 abs_pos = mark_pos = 0; /* keep compiler quiet */
3944 break;
3945 case WINDOW_SEEK_HEAD:
3946 /* rejecting relpos < 0 is easy and simplifies code below */
3947 if (relpos < 0)
3948 goto out_of_frame;
3949 update_frameheadpos(winstate);
3950 abs_pos = winstate->frameheadpos + relpos;
3951 mark_pos = abs_pos;
3952
3953 /*
3954 * Account for exclusion option if one is active, but advance only
3955 * abs_pos not mark_pos. This prevents changes of the current
3956 * row's peer group from resulting in trying to fetch a row before
3957 * some previous mark position.
3958 *
3959 * Note that in some corner cases such as current row being
3960 * outside frame, these calculations are theoretically too simple,
3961 * but it doesn't matter because we'll end up deciding the row is
3962 * out of frame. We do not attempt to avoid fetching rows past
3963 * end of frame; that would happen in some cases anyway.
3964 */
3965 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3966 {
3967 case 0:
3968 /* no adjustment needed */
3969 break;
3971 if (abs_pos >= winstate->currentpos &&
3972 winstate->currentpos >= winstate->frameheadpos)
3973 abs_pos++;
3974 break;
3976 update_grouptailpos(winstate);
3977 if (abs_pos >= winstate->groupheadpos &&
3978 winstate->grouptailpos > winstate->frameheadpos)
3979 {
3980 int64 overlapstart = Max(winstate->groupheadpos,
3981 winstate->frameheadpos);
3982
3983 abs_pos += winstate->grouptailpos - overlapstart;
3984 }
3985 break;
3987 update_grouptailpos(winstate);
3988 if (abs_pos >= winstate->groupheadpos &&
3989 winstate->grouptailpos > winstate->frameheadpos)
3990 {
3991 int64 overlapstart = Max(winstate->groupheadpos,
3992 winstate->frameheadpos);
3993
3994 if (abs_pos == overlapstart)
3995 abs_pos = winstate->currentpos;
3996 else
3997 abs_pos += winstate->grouptailpos - overlapstart - 1;
3998 }
3999 break;
4000 default:
4001 elog(ERROR, "unrecognized frame option state: 0x%x",
4002 winstate->frameOptions);
4003 break;
4004 }
4005 break;
4006 case WINDOW_SEEK_TAIL:
4007 /* rejecting relpos > 0 is easy and simplifies code below */
4008 if (relpos > 0)
4009 goto out_of_frame;
4010 update_frametailpos(winstate);
4011 abs_pos = winstate->frametailpos - 1 + relpos;
4012
4013 /*
4014 * Account for exclusion option if one is active. If there is no
4015 * exclusion, we can safely set the mark at the accessed row. But
4016 * if there is, we can only mark the frame start, because we can't
4017 * be sure how far back in the frame the exclusion might cause us
4018 * to fetch in future. Furthermore, we have to actually check
4019 * against frameheadpos here, since it's unsafe to try to fetch a
4020 * row before frame start if the mark might be there already.
4021 */
4022 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
4023 {
4024 case 0:
4025 /* no adjustment needed */
4026 mark_pos = abs_pos;
4027 break;
4029 if (abs_pos <= winstate->currentpos &&
4030 winstate->currentpos < winstate->frametailpos)
4031 abs_pos--;
4032 update_frameheadpos(winstate);
4033 if (abs_pos < winstate->frameheadpos)
4034 goto out_of_frame;
4035 mark_pos = winstate->frameheadpos;
4036 break;
4038 update_grouptailpos(winstate);
4039 if (abs_pos < winstate->grouptailpos &&
4040 winstate->groupheadpos < winstate->frametailpos)
4041 {
4042 int64 overlapend = Min(winstate->grouptailpos,
4043 winstate->frametailpos);
4044
4045 abs_pos -= overlapend - winstate->groupheadpos;
4046 }
4047 update_frameheadpos(winstate);
4048 if (abs_pos < winstate->frameheadpos)
4049 goto out_of_frame;
4050 mark_pos = winstate->frameheadpos;
4051 break;
4053 update_grouptailpos(winstate);
4054 if (abs_pos < winstate->grouptailpos &&
4055 winstate->groupheadpos < winstate->frametailpos)
4056 {
4057 int64 overlapend = Min(winstate->grouptailpos,
4058 winstate->frametailpos);
4059
4060 if (abs_pos == overlapend - 1)
4061 abs_pos = winstate->currentpos;
4062 else
4063 abs_pos -= overlapend - 1 - winstate->groupheadpos;
4064 }
4065 update_frameheadpos(winstate);
4066 if (abs_pos < winstate->frameheadpos)
4067 goto out_of_frame;
4068 mark_pos = winstate->frameheadpos;
4069 break;
4070 default:
4071 elog(ERROR, "unrecognized frame option state: 0x%x",
4072 winstate->frameOptions);
4073 mark_pos = 0; /* keep compiler quiet */
4074 break;
4075 }
4076 break;
4077 default:
4078 elog(ERROR, "unrecognized window seek type: %d", seektype);
4079 abs_pos = mark_pos = 0; /* keep compiler quiet */
4080 break;
4081 }
4082
4083 if (!window_gettupleslot(winobj, abs_pos, slot))
4084 goto out_of_frame;
4085
4086 /* The code above does not detect all out-of-frame cases, so check */
4087 if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
4088 goto out_of_frame;
4089
4090 if (isout)
4091 *isout = false;
4092 if (set_mark)
4094 econtext->ecxt_outertuple = slot;
4095 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4096 econtext, isnull);
4097
4099 if (isout)
4100 *isout = true;
4101 *isnull = true;
4102 return (Datum) 0;
4103}
#define Min(x, y)
Definition c.h:1093
#define Max(x, y)
Definition c.h:1087
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 3753 of file nodeWindowAgg.c.

3756{
3757 WindowAggState *winstate;
3758 int64 abs_pos;
3760 Datum datum;
3761 bool null_treatment;
3762 int notnull_offset;
3763 int notnull_relpos;
3764 int forward;
3765 bool myisout;
3766
3767 Assert(WindowObjectIsValid(winobj));
3768 winstate = winobj->winstate;
3769
3770 null_treatment = (winobj->ignore_nulls == IGNORE_NULLS && relpos != 0);
3771
3772 switch (seektype)
3773 {
3775 if (null_treatment)
3776 abs_pos = winstate->currentpos;
3777 else
3778 abs_pos = winstate->currentpos + relpos;
3779 break;
3780 case WINDOW_SEEK_HEAD:
3781 if (null_treatment)
3782 abs_pos = 0;
3783 else
3784 abs_pos = relpos;
3785 break;
3786 case WINDOW_SEEK_TAIL:
3787 spool_tuples(winstate, -1);
3788 abs_pos = winstate->spooled_rows - 1 + relpos;
3789 break;
3790 default:
3791 elog(ERROR, "unrecognized window seek type: %d", seektype);
3792 abs_pos = 0; /* keep compiler quiet */
3793 break;
3794 }
3795
3796 /* Easy case if IGNORE NULLS is not specified */
3797 if (!null_treatment)
3798 {
3799 /* get tuple and evaluate in partition */
3800 datum = gettuple_eval_partition(winobj, argno,
3801 abs_pos, isnull, &myisout);
3802 if (!myisout && set_mark)
3803 WinSetMarkPosition(winobj, abs_pos);
3804 if (isout)
3805 *isout = myisout;
3806 return datum;
3807 }
3808
3809 /* Prepare for loop */
3810 notnull_offset = 0;
3812 forward = relpos > 0 ? 1 : -1;
3813 myisout = false;
3814 datum = 0;
3815
3816 /*
3817 * IGNORE NULLS + WINDOW_SEEK_CURRENT + relpos > 0 case, we would fetch
3818 * beyond the current row + relpos to find out the target row. If we mark
3819 * at abs_pos, next call to WinGetFuncArgInPartition or
3820 * WinGetFuncArgInFrame (in case when a window function have multiple
3821 * args) could fail with "cannot fetch row before WindowObject's mark
3822 * position". So keep the mark position at currentpos.
3823 */
3824 if (seektype == WINDOW_SEEK_CURRENT && relpos > 0)
3825 mark_pos = winstate->currentpos;
3826 else
3827 {
3828 /*
3829 * For other cases we have no idea what position of row callers would
3830 * fetch next time. Also for relpos < 0 case (we go backward), we
3831 * cannot set mark either. For those cases we always set mark at 0.
3832 */
3833 mark_pos = 0;
3834 }
3835
3836 /*
3837 * Get the next nonnull value in the partition, moving forward or backward
3838 * until we find a value or reach the partition's end. We cache the
3839 * nullness status because we may repeat this process many times.
3840 */
3841 do
3842 {
3843 int nn_info; /* NOT NULL status */
3844
3845 abs_pos += forward;
3846 if (abs_pos < 0) /* clearly out of partition */
3847 break;
3848
3849 /* check NOT NULL cached info */
3851 if (nn_info == NN_NOTNULL) /* this row is known to be NOT NULL */
3853 else if (nn_info == NN_NULL) /* this row is known to be NULL */
3854 continue; /* keep on moving forward or backward */
3855 else /* need to check NULL or not */
3856 {
3857 /*
3858 * NOT NULL info does not exist yet. Get tuple and evaluate func
3859 * arg in partition. We ignore the return value from
3860 * gettuple_eval_partition because we are just interested in
3861 * whether we are inside or outside of partition, NULL or NOT
3862 * NULL.
3863 */
3865 abs_pos, isnull, &myisout);
3866 if (myisout) /* out of partition? */
3867 break;
3868 if (!*isnull)
3870 /* record the row status */
3871 put_notnull_info(winobj, abs_pos, argno, *isnull);
3872 }
3873 } while (notnull_offset < notnull_relpos);
3874
3875 /* get tuple and evaluate func arg in partition */
3876 datum = gettuple_eval_partition(winobj, argno,
3877 abs_pos, isnull, &myisout);
3878 if (!myisout && set_mark)
3880 if (isout)
3881 *isout = myisout;
3882
3883 return datum;
3884}
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 3613 of file nodeWindowAgg.c.

3614{
3615 Assert(WindowObjectIsValid(winobj));
3616 if (winobj->localmem == NULL)
3617 winobj->localmem =
3619 return winobj->localmem;
3620}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition mcxt.c:1266

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

3644{
3645 Assert(WindowObjectIsValid(winobj));
3646 spool_tuples(winobj->winstate, -1);
3647 return winobj->winstate->spooled_rows;
3648}

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

3697{
3698 WindowAggState *winstate;
3699 WindowAgg *node;
3702 bool res;
3703
3704 Assert(WindowObjectIsValid(winobj));
3705 winstate = winobj->winstate;
3706 node = (WindowAgg *) winstate->ss.ps.plan;
3707
3708 /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3709 if (node->ordNumCols == 0)
3710 return true;
3711
3712 /*
3713 * Note: OK to use temp_slot_2 here because we aren't calling any
3714 * frame-related functions (those tend to clobber temp_slot_2).
3715 */
3716 slot1 = winstate->temp_slot_1;
3717 slot2 = winstate->temp_slot_2;
3718
3719 if (!window_gettupleslot(winobj, pos1, slot1))
3720 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3721 pos1);
3722 if (!window_gettupleslot(winobj, pos2, slot2))
3723 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3724 pos2);
3725
3726 res = are_peers(winstate, slot1, slot2);
3727
3730
3731 return res;
3732}
#define INT64_FORMAT
Definition c.h:636

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

3662{
3663 WindowAggState *winstate;
3664
3665 Assert(WindowObjectIsValid(winobj));
3666 winstate = winobj->winstate;
3667
3668 if (markpos < winobj->markpos)
3669 elog(ERROR, "cannot move WindowObject's mark position backward");
3670 tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3671 if (markpos > winobj->markpos)
3672 {
3673 tuplestore_skiptuples(winstate->buffer,
3674 markpos - winobj->markpos,
3675 true);
3676 winobj->markpos = markpos;
3677 }
3678 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3679 if (markpos > winobj->seekpos)
3680 {
3681 tuplestore_skiptuples(winstate->buffer,
3682 markpos - winobj->seekpos,
3683 true);
3684 winobj->seekpos = markpos;
3685 }
3686}

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