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

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

Macros

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

Typedefs

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

Functions

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

Macro Definition Documentation

◆ INIT_NOT_NULL_INFO_NUM

#define INIT_NOT_NULL_INFO_NUM   128

◆ NN_BITS_PER_MEMBER

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

Definition at line 238 of file nodeWindowAgg.c.

◆ NN_BYTES_TO_POS

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

Definition at line 244 of file nodeWindowAgg.c.

◆ NN_ITEM_PER_VAR

#define NN_ITEM_PER_VAR   (BITS_PER_BYTE / NN_BITS_PER_MEMBER)

Definition at line 240 of file nodeWindowAgg.c.

◆ NN_MASK

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

Definition at line 237 of file nodeWindowAgg.c.

◆ NN_NOTNULL

#define NN_NOTNULL   0x02 /* NOT NULL */

Definition at line 236 of file nodeWindowAgg.c.

◆ NN_NULL

#define NN_NULL   0x01 /* NULL */

Definition at line 235 of file nodeWindowAgg.c.

◆ NN_POS_TO_BYTES

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

Definition at line 242 of file nodeWindowAgg.c.

◆ NN_SHIFT

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

Definition at line 246 of file nodeWindowAgg.c.

◆ NN_UNKNOWN

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

Definition at line 234 of file nodeWindowAgg.c.

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

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

Definition at line 288 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 465 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ are_peers()

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

Definition at line 3231 of file nodeWindowAgg.c.

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

References ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, ExecQualAndReset(), fb(), WindowAggState::ordEqfunction, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, WindowAggState::ss, and WindowAggState::tmpcontext.

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

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1240 of file nodeWindowAgg.c.

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

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

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

Referenced by ExecWindowAgg().

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 709 of file nodeWindowAgg.c.

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

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

Referenced by ExecWindowAgg().

◆ eval_windowfunction()

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

Definition at line 1080 of file nodeWindowAgg.c.

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

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

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2861 of file nodeWindowAgg.c.

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

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

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2548 of file nodeWindowAgg.c.

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

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

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2896 of file nodeWindowAgg.c.

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

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

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot * ExecWindowAgg ( PlanState pstate)
static

Definition at line 2280 of file nodeWindowAgg.c.

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

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

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

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

Definition at line 628 of file nodeWindowAgg.c.

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

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

Referenced by eval_windowaggregates().

◆ get_notnull_info()

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

Definition at line 3576 of file nodeWindowAgg.c.

3577{
3578 uint8 *mbp;
3579 uint8 mb;
3580 int64 bpos;
3581
3582 grow_notnull_info(winobj, pos, argno);
3583 bpos = NN_POS_TO_BYTES(pos);
3584 mbp = winobj->notnull_info[argno];
3585 mb = mbp[bpos];
3586 return (mb >> (NN_SHIFT(pos))) & NN_MASK;
3587}
uint8_t uint8
Definition c.h:622
static void grow_notnull_info(WindowObject winobj, int64 pos, int argno)
#define NN_MASK
#define NN_SHIFT(pos)

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

3210{
3211 Oid typinput,
3212 typioparam;
3213 char *strInitVal;
3214 Datum initVal;
3215
3216 getTypeInputInfo(transtype, &typinput, &typioparam);
3219 typioparam, -1);
3221 return initVal;
3222}
#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 3344 of file nodeWindowAgg.c.

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

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

Referenced by WinGetFuncArgInPartition().

◆ grow_notnull_info()

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

Definition at line 3532 of file nodeWindowAgg.c.

3533{
3534/* initial number of notnull info members */
3535#define INIT_NOT_NULL_INFO_NUM 128
3536
3537 if (pos >= winobj->num_notnull_info[argno])
3538 {
3539 /* We may be called in a short-lived context */
3542
3543 for (;;)
3544 {
3546 (winobj->num_notnull_info[argno]);
3547 Size newsize;
3548
3549 if (oldsize == 0) /* memory has not been allocated yet for this
3550 * arg */
3551 {
3553 winobj->notnull_info[argno] = palloc0(newsize);
3554 }
3555 else
3556 {
3557 newsize = oldsize * 2;
3558 winobj->notnull_info[argno] =
3560 }
3562 if (winobj->num_notnull_info[argno] > pos)
3563 break;
3564 }
3565 MemoryContextSwitchTo(oldcontext);
3566 }
3567}
size_t Size
Definition c.h:689
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 3377 of file nodeWindowAgg.c.

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

3515{
3516 int numargs = perfuncstate->numArguments;
3517
3518 if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3519 {
3520 winobj->notnull_info = palloc0_array(uint8 *, numargs);
3521 winobj->num_notnull_info = palloc0_array(int64, numargs);
3522 }
3523}

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

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

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, WindowAggState::aggcontext, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, WindowFunc::args, build_aggregate_finalfn_expr(), build_aggregate_transfn_expr(), contain_subplans(), contain_volatile_functions(), CurrentMemoryContext, elog, ereport, errcode(), errmsg, ERROR, exprType(), fb(), fmgr_info(), fmgr_info_set_expr, Form_pg_aggregate, Form_pg_proc, format_procedure(), FRAMEOPTION_START_UNBOUNDED_PRECEDING, WindowAggState::frameOptions, FUNC_MAX_ARGS, get_func_name(), get_typlenbyval(), GetAggInitVal(), GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, InvokeFunctionExecuteHook, IsBinaryCoercible(), lfirst, list_length(), object_aclcheck(), OBJECT_FUNCTION, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), resolve_aggregate_transtype(), SearchSysCache1(), SysCacheGetAttr(), and WindowFunc::winfnoid.

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 253 of file nodeWindowAgg.c.

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

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

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ prepare_tuplestore()

static pg_noinline void prepare_tuplestore ( WindowAggState winstate)
static

Definition at line 1132 of file nodeWindowAgg.c.

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

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

Referenced by begin_partition().

◆ put_notnull_info()

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

Definition at line 3597 of file nodeWindowAgg.c.

3598{
3599 uint8 *mbp;
3600 uint8 mb;
3601 int64 bpos;
3602 uint8 val = isnull ? NN_NULL : NN_NOTNULL;
3603 int shift;
3604
3605 grow_notnull_info(winobj, pos, argno);
3606 bpos = NN_POS_TO_BYTES(pos);
3607 mbp = winobj->notnull_info[argno];
3608 mb = mbp[bpos];
3609 shift = NN_SHIFT(pos);
3610 mb &= ~(NN_MASK << shift); /* clear map */
3611 mb |= (val << shift); /* update map */
3612 mbp[bpos] = mb;
3613}
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 1439 of file nodeWindowAgg.c.

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

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

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

◆ row_is_in_frame()

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

Definition at line 1489 of file nodeWindowAgg.c.

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

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

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

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1345 of file nodeWindowAgg.c.

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

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

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

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1605 of file nodeWindowAgg.c.

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

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

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

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1873 of file nodeWindowAgg.c.

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

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

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

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 2143 of file nodeWindowAgg.c.

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

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

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

◆ WinCheckAndInitializeNullTreatment()

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

Definition at line 3628 of file nodeWindowAgg.c.

3631{
3632 Assert(WindowObjectIsValid(winobj));
3634 {
3635 const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
3636
3637 if (!funcname)
3638 elog(ERROR, "could not get function name");
3639 ereport(ERROR,
3641 errmsg("function %s does not allow RESPECT/IGNORE NULLS",
3642 funcname)));
3643 }
3644 else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
3645 winobj->ignore_nulls = IGNORE_NULLS;
3646}
#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 3254 of file nodeWindowAgg.c.

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

4168{
4169 WindowAggState *winstate;
4170 ExprContext *econtext;
4171
4172 Assert(WindowObjectIsValid(winobj));
4173 winstate = winobj->winstate;
4174
4175 econtext = winstate->ss.ps.ps_ExprContext;
4176
4177 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
4178 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
4179 econtext, isnull);
4180}

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

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

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

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

◆ WinGetFuncArgInPartition()

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

Definition at line 3801 of file nodeWindowAgg.c.

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

3662{
3663 Assert(WindowObjectIsValid(winobj));
3664 if (winobj->localmem == NULL)
3665 winobj->localmem =
3667 return winobj->localmem;
3668}
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 3691 of file nodeWindowAgg.c.

3692{
3693 Assert(WindowObjectIsValid(winobj));
3694 spool_tuples(winobj->winstate, -1);
3695 return winobj->winstate->spooled_rows;
3696}

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

3745{
3746 WindowAggState *winstate;
3747 WindowAgg *node;
3750 bool res;
3751
3752 Assert(WindowObjectIsValid(winobj));
3753 winstate = winobj->winstate;
3754 node = (WindowAgg *) winstate->ss.ps.plan;
3755
3756 /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3757 if (node->ordNumCols == 0)
3758 return true;
3759
3760 /*
3761 * Note: OK to use temp_slot_2 here because we aren't calling any
3762 * frame-related functions (those tend to clobber temp_slot_2).
3763 */
3764 slot1 = winstate->temp_slot_1;
3765 slot2 = winstate->temp_slot_2;
3766
3767 if (!window_gettupleslot(winobj, pos1, slot1))
3768 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3769 pos1);
3770 if (!window_gettupleslot(winobj, pos2, slot2))
3771 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3772 pos2);
3773
3774 res = are_peers(winstate, slot1, slot2);
3775
3778
3779 return res;
3780}
#define INT64_FORMAT
Definition c.h:634

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

Referenced by rank_up(), and window_cume_dist().

◆ WinSetMarkPosition()

void WinSetMarkPosition ( WindowObject  winobj,
int64  markpos 
)

Definition at line 3709 of file nodeWindowAgg.c.

3710{
3711 WindowAggState *winstate;
3712
3713 Assert(WindowObjectIsValid(winobj));
3714 winstate = winobj->winstate;
3715
3716 if (markpos < winobj->markpos)
3717 elog(ERROR, "cannot move WindowObject's mark position backward");
3718 tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3719 if (markpos > winobj->markpos)
3720 {
3721 tuplestore_skiptuples(winstate->buffer,
3722 markpos - winobj->markpos,
3723 true);
3724 winobj->markpos = markpos;
3725 }
3726 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3727 if (markpos > winobj->seekpos)
3728 {
3729 tuplestore_skiptuples(winstate->buffer,
3730 markpos - winobj->seekpos,
3731 true);
3732 winobj->seekpos = markpos;
3733 }
3734}

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