PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
nodeWindowAgg.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/optimizer.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/expandeddatum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
#include "windowapi.h"
Include dependency graph for nodeWindowAgg.c:

Go to the source code of this file.

Data Structures

struct  WindowObjectData
 
struct  WindowStatePerFuncData
 
struct  WindowStatePerAggData
 

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 (WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
 
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 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 * WinGetPartitionLocalMemory (WindowObject winobj, Size sz)
 
int64 WinGetCurrentPosition (WindowObject winobj)
 
int64 WinGetPartitionRowCount (WindowObject winobj)
 
void WinSetMarkPosition (WindowObject winobj, int64 markpos)
 
bool WinRowsArePeers (WindowObject winobj, int64 pos1, int64 pos2)
 
Datum WinGetFuncArgInPartition (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgInFrame (WindowObject winobj, int argno, int relpos, int seektype, bool set_mark, bool *isnull, bool *isout)
 
Datum WinGetFuncArgCurrent (WindowObject winobj, int argno, bool *isnull)
 

Typedef Documentation

◆ WindowObjectData

◆ WindowStatePerAggData

◆ WindowStatePerFuncData

Function Documentation

◆ advance_windowaggregate()

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

Definition at line 242 of file nodeWindowAgg.c.

245{
247 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
248 int numArguments = perfuncstate->numArguments;
249 Datum newVal;
250 ListCell *arg;
251 int i;
252 MemoryContext oldContext;
253 ExprContext *econtext = winstate->tmpcontext;
254 ExprState *filter = wfuncstate->aggfilter;
255
256 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
257
258 /* Skip anything FILTERed out */
259 if (filter)
260 {
261 bool isnull;
262 Datum res = ExecEvalExpr(filter, econtext, &isnull);
263
264 if (isnull || !DatumGetBool(res))
265 {
266 MemoryContextSwitchTo(oldContext);
267 return;
268 }
269 }
270
271 /* We start from 1, since the 0th arg will be the transition value */
272 i = 1;
273 foreach(arg, wfuncstate->args)
274 {
275 ExprState *argstate = (ExprState *) lfirst(arg);
276
277 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
278 &fcinfo->args[i].isnull);
279 i++;
280 }
281
282 if (peraggstate->transfn.fn_strict)
283 {
284 /*
285 * For a strict transfn, nothing happens when there's a NULL input; we
286 * just keep the prior transValue. Note transValueCount doesn't
287 * change either.
288 */
289 for (i = 1; i <= numArguments; i++)
290 {
291 if (fcinfo->args[i].isnull)
292 {
293 MemoryContextSwitchTo(oldContext);
294 return;
295 }
296 }
297
298 /*
299 * For strict transition functions with initial value NULL we use the
300 * first non-NULL input as the initial state. (We already checked
301 * that the agg's input type is binary-compatible with its transtype,
302 * so straight copy here is OK.)
303 *
304 * We must copy the datum into aggcontext if it is pass-by-ref. We do
305 * not need to pfree the old transValue, since it's NULL.
306 */
307 if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
308 {
309 MemoryContextSwitchTo(peraggstate->aggcontext);
310 peraggstate->transValue = datumCopy(fcinfo->args[1].value,
311 peraggstate->transtypeByVal,
312 peraggstate->transtypeLen);
313 peraggstate->transValueIsNull = false;
314 peraggstate->transValueCount = 1;
315 MemoryContextSwitchTo(oldContext);
316 return;
317 }
318
319 if (peraggstate->transValueIsNull)
320 {
321 /*
322 * Don't call a strict function with NULL inputs. Note it is
323 * possible to get here despite the above tests, if the transfn is
324 * strict *and* returned a NULL on a prior cycle. If that happens
325 * we will propagate the NULL all the way to the end. That can
326 * only happen if there's no inverse transition function, though,
327 * since we disallow transitions back to NULL when there is one.
328 */
329 MemoryContextSwitchTo(oldContext);
330 Assert(!OidIsValid(peraggstate->invtransfn_oid));
331 return;
332 }
333 }
334
335 /*
336 * OK to call the transition function. Set winstate->curaggcontext while
337 * calling it, for possible use by AggCheckCallContext.
338 */
339 InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
340 numArguments + 1,
341 perfuncstate->winCollation,
342 (Node *) winstate, NULL);
343 fcinfo->args[0].value = peraggstate->transValue;
344 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
345 winstate->curaggcontext = peraggstate->aggcontext;
346 newVal = FunctionCallInvoke(fcinfo);
347 winstate->curaggcontext = NULL;
348
349 /*
350 * Moving-aggregate transition functions must not return null, see
351 * advance_windowaggregate_base().
352 */
353 if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
355 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
356 errmsg("moving-aggregate transition function must not return null")));
357
358 /*
359 * We must track the number of rows included in transValue, since to
360 * remove the last input, advance_windowaggregate_base() mustn't call the
361 * inverse transition function, but simply reset transValue back to its
362 * initial value.
363 */
364 peraggstate->transValueCount++;
365
366 /*
367 * If pass-by-ref datatype, must copy the new value into aggcontext and
368 * free the prior transValue. But if transfn returned a pointer to its
369 * first input, we don't need to do anything. Also, if transfn returned a
370 * pointer to a R/W expanded object that is already a child of the
371 * aggcontext, assume we can adopt that value without copying it. (See
372 * comments for ExecAggCopyTransValue, which this code duplicates.)
373 */
374 if (!peraggstate->transtypeByVal &&
375 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
376 {
377 if (!fcinfo->isnull)
378 {
379 MemoryContextSwitchTo(peraggstate->aggcontext);
381 false,
382 peraggstate->transtypeLen) &&
384 /* do nothing */ ;
385 else
386 newVal = datumCopy(newVal,
387 peraggstate->transtypeByVal,
388 peraggstate->transtypeLen);
389 }
390 if (!peraggstate->transValueIsNull)
391 {
393 false,
394 peraggstate->transtypeLen))
395 DeleteExpandedObject(peraggstate->transValue);
396 else
397 pfree(DatumGetPointer(peraggstate->transValue));
398 }
399 }
400
401 MemoryContextSwitchTo(oldContext);
402 peraggstate->transValue = newVal;
403 peraggstate->transValueIsNull = fcinfo->isnull;
404}
#define Assert(condition)
Definition: c.h:812
#define OidIsValid(objectId)
Definition: c.h:729
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
void DeleteExpandedObject(Datum d)
#define DatumIsReadWriteExpandedObject(d, isnull, typlen)
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
int i
Definition: isn.c:72
void pfree(void *pointer)
Definition: mcxt.c:1521
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
MemoryContext MemoryContextGetParent(MemoryContext context)
Definition: mcxt.c:731
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
void * arg
#define FUNC_MAX_ARGS
#define lfirst(lc)
Definition: pg_list.h:172
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:266
bool fn_strict
Definition: fmgr.h:61
Definition: nodes.h:129
MemoryContext curaggcontext
Definition: execnodes.h:2649
ExprContext * tmpcontext
Definition: execnodes.h:2650
ExprState * aggfilter
Definition: execnodes.h:885
MemoryContext aggcontext
WindowFuncExprState * wfuncstate
Definition: nodeWindowAgg.c:81

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

Referenced by eval_windowaggregates().

◆ advance_windowaggregate_base()

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

Definition at line 419 of file nodeWindowAgg.c.

422{
424 WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
425 int numArguments = perfuncstate->numArguments;
426 Datum newVal;
427 ListCell *arg;
428 int i;
429 MemoryContext oldContext;
430 ExprContext *econtext = winstate->tmpcontext;
431 ExprState *filter = wfuncstate->aggfilter;
432
433 oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
434
435 /* Skip anything FILTERed out */
436 if (filter)
437 {
438 bool isnull;
439 Datum res = ExecEvalExpr(filter, econtext, &isnull);
440
441 if (isnull || !DatumGetBool(res))
442 {
443 MemoryContextSwitchTo(oldContext);
444 return true;
445 }
446 }
447
448 /* We start from 1, since the 0th arg will be the transition value */
449 i = 1;
450 foreach(arg, wfuncstate->args)
451 {
452 ExprState *argstate = (ExprState *) lfirst(arg);
453
454 fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
455 &fcinfo->args[i].isnull);
456 i++;
457 }
458
459 if (peraggstate->invtransfn.fn_strict)
460 {
461 /*
462 * For a strict (inv)transfn, nothing happens when there's a NULL
463 * input; we just keep the prior transValue. Note transValueCount
464 * doesn't change either.
465 */
466 for (i = 1; i <= numArguments; i++)
467 {
468 if (fcinfo->args[i].isnull)
469 {
470 MemoryContextSwitchTo(oldContext);
471 return true;
472 }
473 }
474 }
475
476 /* There should still be an added but not yet removed value */
477 Assert(peraggstate->transValueCount > 0);
478
479 /*
480 * In moving-aggregate mode, the state must never be NULL, except possibly
481 * before any rows have been aggregated (which is surely not the case at
482 * this point). This restriction allows us to interpret a NULL result
483 * from the inverse function as meaning "sorry, can't do an inverse
484 * transition in this case". We already checked this in
485 * advance_windowaggregate, but just for safety, check again.
486 */
487 if (peraggstate->transValueIsNull)
488 elog(ERROR, "aggregate transition value is NULL before inverse transition");
489
490 /*
491 * We mustn't use the inverse transition function to remove the last
492 * input. Doing so would yield a non-NULL state, whereas we should be in
493 * the initial state afterwards which may very well be NULL. So instead,
494 * we simply re-initialize the aggregate in this case.
495 */
496 if (peraggstate->transValueCount == 1)
497 {
498 MemoryContextSwitchTo(oldContext);
500 &winstate->perfunc[peraggstate->wfuncno],
501 peraggstate);
502 return true;
503 }
504
505 /*
506 * OK to call the inverse transition function. Set
507 * winstate->curaggcontext while calling it, for possible use by
508 * AggCheckCallContext.
509 */
510 InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
511 numArguments + 1,
512 perfuncstate->winCollation,
513 (Node *) winstate, NULL);
514 fcinfo->args[0].value = peraggstate->transValue;
515 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
516 winstate->curaggcontext = peraggstate->aggcontext;
517 newVal = FunctionCallInvoke(fcinfo);
518 winstate->curaggcontext = NULL;
519
520 /*
521 * If the function returns NULL, report failure, forcing a restart.
522 */
523 if (fcinfo->isnull)
524 {
525 MemoryContextSwitchTo(oldContext);
526 return false;
527 }
528
529 /* Update number of rows included in transValue */
530 peraggstate->transValueCount--;
531
532 /*
533 * If pass-by-ref datatype, must copy the new value into aggcontext and
534 * free the prior transValue. But if invtransfn returned a pointer to its
535 * first input, we don't need to do anything. Also, if invtransfn
536 * returned a pointer to a R/W expanded object that is already a child of
537 * the aggcontext, assume we can adopt that value without copying it. (See
538 * comments for ExecAggCopyTransValue, which this code duplicates.)
539 *
540 * Note: the checks for null values here will never fire, but it seems
541 * best to have this stanza look just like advance_windowaggregate.
542 */
543 if (!peraggstate->transtypeByVal &&
544 DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
545 {
546 if (!fcinfo->isnull)
547 {
548 MemoryContextSwitchTo(peraggstate->aggcontext);
550 false,
551 peraggstate->transtypeLen) &&
553 /* do nothing */ ;
554 else
555 newVal = datumCopy(newVal,
556 peraggstate->transtypeByVal,
557 peraggstate->transtypeLen);
558 }
559 if (!peraggstate->transValueIsNull)
560 {
562 false,
563 peraggstate->transtypeLen))
564 DeleteExpandedObject(peraggstate->transValue);
565 else
566 pfree(DatumGetPointer(peraggstate->transValue));
567 }
568 }
569
570 MemoryContextSwitchTo(oldContext);
571 peraggstate->transValue = newVal;
572 peraggstate->transValueIsNull = fcinfo->isnull;
573
574 return true;
575}
#define elog(elevel,...)
Definition: elog.h:225
static void initialize_windowaggregate(WindowAggState *winstate, WindowStatePerFunc perfuncstate, WindowStatePerAgg peraggstate)
WindowStatePerFunc perfunc
Definition: execnodes.h:2597

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

Referenced by eval_windowaggregates().

◆ are_peers()

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

Definition at line 3109 of file nodeWindowAgg.c.

3111{
3112 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
3113 ExprContext *econtext = winstate->tmpcontext;
3114
3115 /* If no ORDER BY, all rows are peers with each other */
3116 if (node->ordNumCols == 0)
3117 return true;
3118
3119 econtext->ecxt_outertuple = slot1;
3120 econtext->ecxt_innertuple = slot2;
3121 return ExecQualAndReset(winstate->ordEqfunction, econtext);
3122}
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:453
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
Plan * plan
Definition: execnodes.h:1126
PlanState ps
Definition: execnodes.h:1573
ScanState ss
Definition: execnodes.h:2590
ExprState * ordEqfunction
Definition: execnodes.h:2600
int ordNumCols
Definition: plannodes.h:1059

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

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

◆ begin_partition()

static void begin_partition ( WindowAggState winstate)
static

Definition at line 1193 of file nodeWindowAgg.c.

1194{
1195 PlanState *outerPlan = outerPlanState(winstate);
1196 int numfuncs = winstate->numfuncs;
1197
1198 winstate->partition_spooled = false;
1199 winstate->framehead_valid = false;
1200 winstate->frametail_valid = false;
1201 winstate->grouptail_valid = false;
1202 winstate->spooled_rows = 0;
1203 winstate->currentpos = 0;
1204 winstate->frameheadpos = 0;
1205 winstate->frametailpos = 0;
1206 winstate->currentgroup = 0;
1207 winstate->frameheadgroup = 0;
1208 winstate->frametailgroup = 0;
1209 winstate->groupheadpos = 0;
1210 winstate->grouptailpos = -1; /* see update_grouptailpos */
1211 ExecClearTuple(winstate->agg_row_slot);
1212 if (winstate->framehead_slot)
1213 ExecClearTuple(winstate->framehead_slot);
1214 if (winstate->frametail_slot)
1215 ExecClearTuple(winstate->frametail_slot);
1216
1217 /*
1218 * If this is the very first partition, we need to fetch the first input
1219 * row to store in first_part_slot.
1220 */
1221 if (TupIsNull(winstate->first_part_slot))
1222 {
1224
1225 if (!TupIsNull(outerslot))
1226 ExecCopySlot(winstate->first_part_slot, outerslot);
1227 else
1228 {
1229 /* outer plan is empty, so we have nothing to do */
1230 winstate->partition_spooled = true;
1231 winstate->more_partitions = false;
1232 return;
1233 }
1234 }
1235
1236 /* Create new tuplestore if not done already. */
1237 if (unlikely(winstate->buffer == NULL))
1238 prepare_tuplestore(winstate);
1239
1240 winstate->next_partition = false;
1241
1242 if (winstate->numaggs > 0)
1243 {
1244 WindowObject agg_winobj = winstate->agg_winobj;
1245
1246 /* reset mark and see positions for aggregate functions */
1247 agg_winobj->markpos = -1;
1248 agg_winobj->seekpos = -1;
1249
1250 /* Also reset the row counters for aggregates */
1251 winstate->aggregatedbase = 0;
1252 winstate->aggregatedupto = 0;
1253 }
1254
1255 /* reset mark and seek positions for each real window function */
1256 for (int i = 0; i < numfuncs; i++)
1257 {
1258 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1259
1260 if (!perfuncstate->plain_agg)
1261 {
1262 WindowObject winobj = perfuncstate->winobj;
1263
1264 winobj->markpos = -1;
1265 winobj->seekpos = -1;
1266 }
1267 }
1268
1269 /*
1270 * Store the first tuple into the tuplestore (it's always available now;
1271 * we either read it above, or saved it at the end of previous partition)
1272 */
1273 tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
1274 winstate->spooled_rows++;
1275}
#define unlikely(x)
Definition: c.h:330
#define outerPlanState(node)
Definition: execnodes.h:1222
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:267
static pg_noinline void prepare_tuplestore(WindowAggState *winstate)
#define outerPlan(node)
Definition: plannodes.h:183
int64 aggregatedbase
Definition: execnodes.h:2612
int64 frametailgroup
Definition: execnodes.h:2643
int64 frameheadgroup
Definition: execnodes.h:2642
TupleTableSlot * framehead_slot
Definition: execnodes.h:2667
bool next_partition
Definition: execnodes.h:2655
bool frametail_valid
Definition: execnodes.h:2660
bool partition_spooled
Definition: execnodes.h:2653
int64 spooled_rows
Definition: execnodes.h:2606
int64 frameheadpos
Definition: execnodes.h:2608
bool more_partitions
Definition: execnodes.h:2656
int64 grouptailpos
Definition: execnodes.h:2645
int64 currentgroup
Definition: execnodes.h:2641
TupleTableSlot * frametail_slot
Definition: execnodes.h:2668
Tuplestorestate * buffer
Definition: execnodes.h:2601
TupleTableSlot * agg_row_slot
Definition: execnodes.h:2671
struct WindowObjectData * agg_winobj
Definition: execnodes.h:2611
bool framehead_valid
Definition: execnodes.h:2658
int64 groupheadpos
Definition: execnodes.h:2644
bool grouptail_valid
Definition: execnodes.h:2662
int64 currentpos
Definition: execnodes.h:2607
int64 frametailpos
Definition: execnodes.h:2609
TupleTableSlot * first_part_slot
Definition: execnodes.h:2665
int64 aggregatedupto
Definition: execnodes.h:2613
void tuplestore_puttupleslot(Tuplestorestate *state, TupleTableSlot *slot)
Definition: tuplestore.c:742
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
#define TupIsNull(slot)
Definition: tuptable.h:306
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509

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

Referenced by ExecWindowAgg().

◆ calculate_frame_offsets()

static pg_noinline void calculate_frame_offsets ( PlanState pstate)
static

Definition at line 2082 of file nodeWindowAgg.c.

2083{
2084 WindowAggState *winstate = castNode(WindowAggState, pstate);
2085 ExprContext *econtext;
2086 int frameOptions = winstate->frameOptions;
2087 Datum value;
2088 bool isnull;
2089 int16 len;
2090 bool byval;
2091
2092 /* Ensure we've not been called before for this scan */
2093 Assert(winstate->all_first);
2094
2095 econtext = winstate->ss.ps.ps_ExprContext;
2096
2097 if (frameOptions & FRAMEOPTION_START_OFFSET)
2098 {
2099 Assert(winstate->startOffset != NULL);
2101 econtext,
2102 &isnull);
2103 if (isnull)
2104 ereport(ERROR,
2105 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2106 errmsg("frame starting offset must not be null")));
2107 /* copy value into query-lifespan context */
2109 &len,
2110 &byval);
2111 winstate->startOffsetValue = datumCopy(value, byval, len);
2112 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2113 {
2114 /* value is known to be int8 */
2115 int64 offset = DatumGetInt64(value);
2116
2117 if (offset < 0)
2118 ereport(ERROR,
2119 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2120 errmsg("frame starting offset must not be negative")));
2121 }
2122 }
2123
2124 if (frameOptions & FRAMEOPTION_END_OFFSET)
2125 {
2126 Assert(winstate->endOffset != NULL);
2128 econtext,
2129 &isnull);
2130 if (isnull)
2131 ereport(ERROR,
2132 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2133 errmsg("frame ending offset must not be null")));
2134 /* copy value into query-lifespan context */
2135 get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
2136 &len,
2137 &byval);
2138 winstate->endOffsetValue = datumCopy(value, byval, len);
2139 if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
2140 {
2141 /* value is known to be int8 */
2142 int64 offset = DatumGetInt64(value);
2143
2144 if (offset < 0)
2145 ereport(ERROR,
2146 (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
2147 errmsg("frame ending offset must not be negative")));
2148 }
2149 }
2150 winstate->all_first = false;
2151}
int64_t int64
Definition: c.h:482
int16_t int16
Definition: c.h:480
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:361
static struct @161 value
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition: lsyscache.c:2251
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define FRAMEOPTION_END_OFFSET
Definition: parsenodes.h:604
#define FRAMEOPTION_START_OFFSET
Definition: parsenodes.h:602
#define FRAMEOPTION_GROUPS
Definition: parsenodes.h:586
#define FRAMEOPTION_ROWS
Definition: parsenodes.h:585
const void size_t len
static int64 DatumGetInt64(Datum X)
Definition: postgres.h:385
Expr * expr
Definition: execnodes.h:111
ExprContext * ps_ExprContext
Definition: execnodes.h:1165
ExprState * endOffset
Definition: execnodes.h:2618
Datum startOffsetValue
Definition: execnodes.h:2619
Datum endOffsetValue
Definition: execnodes.h:2620
ExprState * startOffset
Definition: execnodes.h:2617

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

Referenced by ExecWindowAgg().

◆ eval_windowaggregates()

static void eval_windowaggregates ( WindowAggState winstate)
static

Definition at line 663 of file nodeWindowAgg.c.

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

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

Referenced by ExecWindowAgg().

◆ eval_windowfunction()

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

Definition at line 1033 of file nodeWindowAgg.c.

1035{
1036 LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
1037 MemoryContext oldContext;
1038
1040
1041 /*
1042 * We don't pass any normal arguments to a window function, but we do pass
1043 * it the number of arguments, in order to permit window function
1044 * implementations to support varying numbers of arguments. The real info
1045 * goes through the WindowObject, which is passed via fcinfo->context.
1046 */
1047 InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
1048 perfuncstate->numArguments,
1049 perfuncstate->winCollation,
1050 (Node *) perfuncstate->winobj, NULL);
1051 /* Just in case, make all the regular argument slots be null */
1052 for (int argno = 0; argno < perfuncstate->numArguments; argno++)
1053 fcinfo->args[argno].isnull = true;
1054 /* Window functions don't have a current aggregate context, either */
1055 winstate->curaggcontext = NULL;
1056
1057 *result = FunctionCallInvoke(fcinfo);
1058 *isnull = fcinfo->isnull;
1059
1060 /*
1061 * The window function might have returned a pass-by-ref result that's
1062 * just a pointer into one of the WindowObject's temporary slots. That's
1063 * not a problem if it's the only window function using the WindowObject;
1064 * but if there's more than one function, we'd better copy the result to
1065 * ensure it's not clobbered by later window functions.
1066 */
1067 if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
1068 winstate->numfuncs > 1)
1069 *result = datumCopy(*result,
1070 perfuncstate->resulttypeByVal,
1071 perfuncstate->resulttypeLen);
1072
1073 MemoryContextSwitchTo(oldContext);
1074}

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

Referenced by ExecWindowAgg().

◆ ExecEndWindowAgg()

void ExecEndWindowAgg ( WindowAggState node)

Definition at line 2739 of file nodeWindowAgg.c.

2740{
2742 int i;
2743
2744 if (node->buffer != NULL)
2745 {
2746 tuplestore_end(node->buffer);
2747
2748 /* nullify so that release_partition skips the tuplestore_clear() */
2749 node->buffer = NULL;
2750 }
2751
2752 release_partition(node);
2753
2754 for (i = 0; i < node->numaggs; i++)
2755 {
2756 if (node->peragg[i].aggcontext != node->aggcontext)
2758 }
2761
2762 pfree(node->perfunc);
2763 pfree(node->peragg);
2764
2765 outerPlan = outerPlanState(node);
2767}
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:562
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
static void release_partition(WindowAggState *winstate)
MemoryContext partcontext
Definition: execnodes.h:2647
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:492

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

Referenced by ExecEndNode().

◆ ExecInitWindowAgg()

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

Definition at line 2431 of file nodeWindowAgg.c.

2432{
2433 WindowAggState *winstate;
2434 Plan *outerPlan;
2435 ExprContext *econtext;
2436 ExprContext *tmpcontext;
2437 WindowStatePerFunc perfunc;
2438 WindowStatePerAgg peragg;
2439 int frameOptions = node->frameOptions;
2440 int numfuncs,
2441 wfuncno,
2442 numaggs,
2443 aggno;
2444 TupleDesc scanDesc;
2445 ListCell *l;
2446
2447 /* check for unsupported flags */
2448 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
2449
2450 /*
2451 * create state structure
2452 */
2453 winstate = makeNode(WindowAggState);
2454 winstate->ss.ps.plan = (Plan *) node;
2455 winstate->ss.ps.state = estate;
2456 winstate->ss.ps.ExecProcNode = ExecWindowAgg;
2457
2458 /* copy frame options to state node for easy access */
2459 winstate->frameOptions = frameOptions;
2460
2461 /*
2462 * Create expression contexts. We need two, one for per-input-tuple
2463 * processing and one for per-output-tuple processing. We cheat a little
2464 * by using ExecAssignExprContext() to build both.
2465 */
2466 ExecAssignExprContext(estate, &winstate->ss.ps);
2467 tmpcontext = winstate->ss.ps.ps_ExprContext;
2468 winstate->tmpcontext = tmpcontext;
2469 ExecAssignExprContext(estate, &winstate->ss.ps);
2470
2471 /* Create long-lived context for storage of partition-local memory etc */
2472 winstate->partcontext =
2474 "WindowAgg Partition",
2476
2477 /*
2478 * Create mid-lived context for aggregate trans values etc.
2479 *
2480 * Note that moving aggregates each use their own private context, not
2481 * this one.
2482 */
2483 winstate->aggcontext =
2485 "WindowAgg Aggregates",
2487
2488 /* Only the top-level WindowAgg may have a qual */
2489 Assert(node->plan.qual == NIL || node->topWindow);
2490
2491 /* Initialize the qual */
2492 winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
2493 (PlanState *) winstate);
2494
2495 /*
2496 * Setup the run condition, if we received one from the query planner.
2497 * When set, this may allow us to move into pass-through mode so that we
2498 * don't have to perform any further evaluation of WindowFuncs in the
2499 * current partition or possibly stop returning tuples altogether when all
2500 * tuples are in the same partition.
2501 */
2502 winstate->runcondition = ExecInitQual(node->runCondition,
2503 (PlanState *) winstate);
2504
2505 /*
2506 * When we're not the top-level WindowAgg node or we are but have a
2507 * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
2508 * modes when the runCondition becomes false.
2509 */
2510 winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
2511
2512 /* remember if we're the top-window or we are below the top-window */
2513 winstate->top_window = node->topWindow;
2514
2515 /*
2516 * initialize child nodes
2517 */
2518 outerPlan = outerPlan(node);
2519 outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
2520
2521 /*
2522 * initialize source tuple type (which is also the tuple type that we'll
2523 * store in the tuplestore and use in all our working slots).
2524 */
2526 scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
2527
2528 /* the outer tuple isn't the child's tuple, but always a minimal tuple */
2529 winstate->ss.ps.outeropsset = true;
2530 winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
2531 winstate->ss.ps.outeropsfixed = true;
2532
2533 /*
2534 * tuple table initialization
2535 */
2536 winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2538 winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2540 winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
2542 winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
2544
2545 /*
2546 * create frame head and tail slots only if needed (must create slots in
2547 * exactly the same cases that update_frameheadpos and update_frametailpos
2548 * need them)
2549 */
2550 winstate->framehead_slot = winstate->frametail_slot = NULL;
2551
2552 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
2553 {
2554 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
2555 node->ordNumCols != 0) ||
2556 (frameOptions & FRAMEOPTION_START_OFFSET))
2557 winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2559 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
2560 node->ordNumCols != 0) ||
2561 (frameOptions & FRAMEOPTION_END_OFFSET))
2562 winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
2564 }
2565
2566 /*
2567 * Initialize result slot, type and projection.
2568 */
2570 ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
2571
2572 /* Set up data for comparing tuples */
2573 if (node->partNumCols > 0)
2574 winstate->partEqfunction =
2575 execTuplesMatchPrepare(scanDesc,
2576 node->partNumCols,
2577 node->partColIdx,
2578 node->partOperators,
2579 node->partCollations,
2580 &winstate->ss.ps);
2581
2582 if (node->ordNumCols > 0)
2583 winstate->ordEqfunction =
2584 execTuplesMatchPrepare(scanDesc,
2585 node->ordNumCols,
2586 node->ordColIdx,
2587 node->ordOperators,
2588 node->ordCollations,
2589 &winstate->ss.ps);
2590
2591 /*
2592 * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
2593 */
2594 numfuncs = winstate->numfuncs;
2595 numaggs = winstate->numaggs;
2596 econtext = winstate->ss.ps.ps_ExprContext;
2597 econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numfuncs);
2598 econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numfuncs);
2599
2600 /*
2601 * allocate per-wfunc/per-agg state information.
2602 */
2603 perfunc = (WindowStatePerFunc) palloc0(sizeof(WindowStatePerFuncData) * numfuncs);
2604 peragg = (WindowStatePerAgg) palloc0(sizeof(WindowStatePerAggData) * numaggs);
2605 winstate->perfunc = perfunc;
2606 winstate->peragg = peragg;
2607
2608 wfuncno = -1;
2609 aggno = -1;
2610 foreach(l, winstate->funcs)
2611 {
2612 WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
2613 WindowFunc *wfunc = wfuncstate->wfunc;
2614 WindowStatePerFunc perfuncstate;
2615 AclResult aclresult;
2616 int i;
2617
2618 if (wfunc->winref != node->winref) /* planner screwed up? */
2619 elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
2620 wfunc->winref, node->winref);
2621
2622 /* Look for a previous duplicate window function */
2623 for (i = 0; i <= wfuncno; i++)
2624 {
2625 if (equal(wfunc, perfunc[i].wfunc) &&
2626 !contain_volatile_functions((Node *) wfunc))
2627 break;
2628 }
2629 if (i <= wfuncno)
2630 {
2631 /* Found a match to an existing entry, so just mark it */
2632 wfuncstate->wfuncno = i;
2633 continue;
2634 }
2635
2636 /* Nope, so assign a new PerAgg record */
2637 perfuncstate = &perfunc[++wfuncno];
2638
2639 /* Mark WindowFunc state node with assigned index in the result array */
2640 wfuncstate->wfuncno = wfuncno;
2641
2642 /* Check permission to call window function */
2643 aclresult = object_aclcheck(ProcedureRelationId, wfunc->winfnoid, GetUserId(),
2644 ACL_EXECUTE);
2645 if (aclresult != ACLCHECK_OK)
2647 get_func_name(wfunc->winfnoid));
2648 InvokeFunctionExecuteHook(wfunc->winfnoid);
2649
2650 /* Fill in the perfuncstate data */
2651 perfuncstate->wfuncstate = wfuncstate;
2652 perfuncstate->wfunc = wfunc;
2653 perfuncstate->numArguments = list_length(wfuncstate->args);
2654 perfuncstate->winCollation = wfunc->inputcollid;
2655
2656 get_typlenbyval(wfunc->wintype,
2657 &perfuncstate->resulttypeLen,
2658 &perfuncstate->resulttypeByVal);
2659
2660 /*
2661 * If it's really just a plain aggregate function, we'll emulate the
2662 * Agg environment for it.
2663 */
2664 perfuncstate->plain_agg = wfunc->winagg;
2665 if (wfunc->winagg)
2666 {
2667 WindowStatePerAgg peraggstate;
2668
2669 perfuncstate->aggno = ++aggno;
2670 peraggstate = &winstate->peragg[aggno];
2671 initialize_peragg(winstate, wfunc, peraggstate);
2672 peraggstate->wfuncno = wfuncno;
2673 }
2674 else
2675 {
2677
2678 winobj->winstate = winstate;
2679 winobj->argstates = wfuncstate->args;
2680 winobj->localmem = NULL;
2681 perfuncstate->winobj = winobj;
2682
2683 /* It's a real window function, so set up to call it. */
2684 fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
2685 econtext->ecxt_per_query_memory);
2686 fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
2687 }
2688 }
2689
2690 /* Update numfuncs, numaggs to match number of unique functions found */
2691 winstate->numfuncs = wfuncno + 1;
2692 winstate->numaggs = aggno + 1;
2693
2694 /* Set up WindowObject for aggregates, if needed */
2695 if (winstate->numaggs > 0)
2696 {
2698
2699 agg_winobj->winstate = winstate;
2700 agg_winobj->argstates = NIL;
2701 agg_winobj->localmem = NULL;
2702 /* make sure markptr = -1 to invalidate. It may not get used */
2703 agg_winobj->markptr = -1;
2704 agg_winobj->readptr = -1;
2705 winstate->agg_winobj = agg_winobj;
2706 }
2707
2708 /* Set the status to running */
2709 winstate->status = WINDOWAGG_RUN;
2710
2711 /* initialize frame bound offset expressions */
2712 winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
2713 (PlanState *) winstate);
2714 winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
2715 (PlanState *) winstate);
2716
2717 /* Lookup in_range support functions if needed */
2718 if (OidIsValid(node->startInRangeFunc))
2719 fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
2720 if (OidIsValid(node->endInRangeFunc))
2721 fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
2722 winstate->inRangeColl = node->inRangeColl;
2723 winstate->inRangeAsc = node->inRangeAsc;
2724 winstate->inRangeNullsFirst = node->inRangeNullsFirst;
2725
2726 winstate->all_first = true;
2727 winstate->partition_spooled = false;
2728 winstate->more_partitions = false;
2729 winstate->next_partition = true;
2730
2731 return winstate;
2732}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3810
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:537
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:138
ExprState * ExecInitQual(List *qual, PlanState *parent)
Definition: execExpr.c:224
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:58
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:2018
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1986
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:86
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition: execUtils.c:704
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:485
void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc)
Definition: execUtils.c:583
struct WindowStatePerAggData * WindowStatePerAgg
Definition: execnodes.h:2574
@ WINDOWAGG_RUN
Definition: execnodes.h:2582
struct WindowStatePerFuncData * WindowStatePerFunc
Definition: execnodes.h:2573
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
#define EXEC_FLAG_MARK
Definition: executor.h:69
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:135
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1608
void * palloc0(Size size)
Definition: mcxt.c:1347
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
Oid GetUserId(void)
Definition: miscinit.c:517
static TupleTableSlot * ExecWindowAgg(PlanState *pstate)
static WindowStatePerAggData * initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, WindowStatePerAgg peraggstate)
#define makeNode(_type_)
Definition: nodes.h:155
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:213
#define FRAMEOPTION_START_CURRENT_ROW
Definition: parsenodes.h:592
@ OBJECT_FUNCTION
Definition: parsenodes.h:2287
#define FRAMEOPTION_RANGE
Definition: parsenodes.h:584
#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:265
bool outeropsset
Definition: execnodes.h:1209
const TupleTableSlotOps * outerops
Definition: execnodes.h:1201
ExprState * qual
Definition: execnodes.h:1147
bool outeropsfixed
Definition: execnodes.h:1205
EState * state
Definition: execnodes.h:1128
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1132
List * qual
Definition: plannodes.h:154
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1576
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
FmgrInfo endInRangeFunc
Definition: execnodes.h:2624
FmgrInfo startInRangeFunc
Definition: execnodes.h:2623
ExprState * runcondition
Definition: execnodes.h:2635
TupleTableSlot * temp_slot_2
Definition: execnodes.h:2673
WindowAggStatus status
Definition: execnodes.h:2614
bool inRangeNullsFirst
Definition: execnodes.h:2627
ExprState * partEqfunction
Definition: execnodes.h:2599
bool use_pass_through
Definition: execnodes.h:2630
int partNumCols
Definition: plannodes.h:1047
Oid endInRangeFunc
Definition: plannodes.h:1091
Node * endOffset
Definition: plannodes.h:1077
bool topWindow
Definition: plannodes.h:1106
Plan plan
Definition: plannodes.h:1041
Oid inRangeColl
Definition: plannodes.h:1094
Node * startOffset
Definition: plannodes.h:1074
List * runCondition
Definition: plannodes.h:1080
Oid startInRangeFunc
Definition: plannodes.h:1088
bool inRangeAsc
Definition: plannodes.h:1097
Index winref
Definition: plannodes.h:1044
bool inRangeNullsFirst
Definition: plannodes.h:1100
int frameOptions
Definition: plannodes.h:1071
WindowFunc * wfunc
Definition: execnodes.h:883
Index winref
Definition: primnodes.h:581
WindowAggState * winstate
Definition: nodeWindowAgg.c:65

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

Referenced by ExecInitNode().

◆ ExecReScanWindowAgg()

void ExecReScanWindowAgg ( WindowAggState node)

Definition at line 2774 of file nodeWindowAgg.c.

2775{
2777 ExprContext *econtext = node->ss.ps.ps_ExprContext;
2778
2779 node->status = WINDOWAGG_RUN;
2780 node->all_first = true;
2781
2782 /* release tuplestore et al */
2783 release_partition(node);
2784
2785 /* release all temp tuples, but especially first_part_slot */
2791 if (node->framehead_slot)
2793 if (node->frametail_slot)
2795
2796 /* Forget current wfunc values */
2797 MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
2798 MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
2799
2800 /*
2801 * if chgParam of subnode is not null then plan will be re-scanned by
2802 * first ExecProcNode.
2803 */
2804 if (outerPlan->chgParam == NULL)
2806}
#define MemSet(start, val, len)
Definition: c.h:974
void ExecReScan(PlanState *node)
Definition: execAmi.c:76

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

Referenced by ExecReScan().

◆ ExecWindowAgg()

static TupleTableSlot * ExecWindowAgg ( PlanState pstate)
static

Definition at line 2163 of file nodeWindowAgg.c.

2164{
2165 WindowAggState *winstate = castNode(WindowAggState, pstate);
2166 TupleTableSlot *slot;
2167 ExprContext *econtext;
2168 int i;
2169 int numfuncs;
2170
2172
2173 if (winstate->status == WINDOWAGG_DONE)
2174 return NULL;
2175
2176 /*
2177 * Compute frame offset values, if any, during first call (or after a
2178 * rescan). These are assumed to hold constant throughout the scan; if
2179 * user gives us a volatile expression, we'll only use its initial value.
2180 */
2181 if (unlikely(winstate->all_first))
2183
2184 /* We need to loop as the runCondition or qual may filter out tuples */
2185 for (;;)
2186 {
2187 if (winstate->next_partition)
2188 {
2189 /* Initialize for first partition and set current row = 0 */
2190 begin_partition(winstate);
2191 /* If there are no input rows, we'll detect that and exit below */
2192 }
2193 else
2194 {
2195 /* Advance current row within partition */
2196 winstate->currentpos++;
2197 /* This might mean that the frame moves, too */
2198 winstate->framehead_valid = false;
2199 winstate->frametail_valid = false;
2200 /* we don't need to invalidate grouptail here; see below */
2201 }
2202
2203 /*
2204 * Spool all tuples up to and including the current row, if we haven't
2205 * already
2206 */
2207 spool_tuples(winstate, winstate->currentpos);
2208
2209 /* Move to the next partition if we reached the end of this partition */
2210 if (winstate->partition_spooled &&
2211 winstate->currentpos >= winstate->spooled_rows)
2212 {
2213 release_partition(winstate);
2214
2215 if (winstate->more_partitions)
2216 {
2217 begin_partition(winstate);
2218 Assert(winstate->spooled_rows > 0);
2219
2220 /* Come out of pass-through mode when changing partition */
2221 winstate->status = WINDOWAGG_RUN;
2222 }
2223 else
2224 {
2225 /* No further partitions? We're done */
2226 winstate->status = WINDOWAGG_DONE;
2227 return NULL;
2228 }
2229 }
2230
2231 /* final output execution is in ps_ExprContext */
2232 econtext = winstate->ss.ps.ps_ExprContext;
2233
2234 /* Clear the per-output-tuple context for current row */
2235 ResetExprContext(econtext);
2236
2237 /*
2238 * Read the current row from the tuplestore, and save in
2239 * ScanTupleSlot. (We can't rely on the outerplan's output slot
2240 * because we may have to read beyond the current row. Also, we have
2241 * to actually copy the row out of the tuplestore, since window
2242 * function evaluation might cause the tuplestore to dump its state to
2243 * disk.)
2244 *
2245 * In GROUPS mode, or when tracking a group-oriented exclusion clause,
2246 * we must also detect entering a new peer group and update associated
2247 * state when that happens. We use temp_slot_2 to temporarily hold
2248 * the previous row for this purpose.
2249 *
2250 * Current row must be in the tuplestore, since we spooled it above.
2251 */
2253 if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
2256 winstate->currentpos > 0)
2257 {
2258 ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
2259 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2260 winstate->ss.ss_ScanTupleSlot))
2261 elog(ERROR, "unexpected end of tuplestore");
2262 if (!are_peers(winstate, winstate->temp_slot_2,
2263 winstate->ss.ss_ScanTupleSlot))
2264 {
2265 winstate->currentgroup++;
2266 winstate->groupheadpos = winstate->currentpos;
2267 winstate->grouptail_valid = false;
2268 }
2269 ExecClearTuple(winstate->temp_slot_2);
2270 }
2271 else
2272 {
2273 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2274 winstate->ss.ss_ScanTupleSlot))
2275 elog(ERROR, "unexpected end of tuplestore");
2276 }
2277
2278 /* don't evaluate the window functions when we're in pass-through mode */
2279 if (winstate->status == WINDOWAGG_RUN)
2280 {
2281 /*
2282 * Evaluate true window functions
2283 */
2284 numfuncs = winstate->numfuncs;
2285 for (i = 0; i < numfuncs; i++)
2286 {
2287 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
2288
2289 if (perfuncstate->plain_agg)
2290 continue;
2291 eval_windowfunction(winstate, perfuncstate,
2292 &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
2293 &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
2294 }
2295
2296 /*
2297 * Evaluate aggregates
2298 */
2299 if (winstate->numaggs > 0)
2300 eval_windowaggregates(winstate);
2301 }
2302
2303 /*
2304 * If we have created auxiliary read pointers for the frame or group
2305 * boundaries, force them to be kept up-to-date, because we don't know
2306 * whether the window function(s) will do anything that requires that.
2307 * Failing to advance the pointers would result in being unable to
2308 * trim data from the tuplestore, which is bad. (If we could know in
2309 * advance whether the window functions will use frame boundary info,
2310 * we could skip creating these pointers in the first place ... but
2311 * unfortunately the window function API doesn't require that.)
2312 */
2313 if (winstate->framehead_ptr >= 0)
2314 update_frameheadpos(winstate);
2315 if (winstate->frametail_ptr >= 0)
2316 update_frametailpos(winstate);
2317 if (winstate->grouptail_ptr >= 0)
2318 update_grouptailpos(winstate);
2319
2320 /*
2321 * Truncate any no-longer-needed rows from the tuplestore.
2322 */
2323 tuplestore_trim(winstate->buffer);
2324
2325 /*
2326 * Form and return a projection tuple using the windowfunc results and
2327 * the current row. Setting ecxt_outertuple arranges that any Vars
2328 * will be evaluated with respect to that row.
2329 */
2330 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
2331
2332 slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
2333
2334 if (winstate->status == WINDOWAGG_RUN)
2335 {
2336 econtext->ecxt_scantuple = slot;
2337
2338 /*
2339 * Now evaluate the run condition to see if we need to go into
2340 * pass-through mode, or maybe stop completely.
2341 */
2342 if (!ExecQual(winstate->runcondition, econtext))
2343 {
2344 /*
2345 * Determine which mode to move into. If there is no
2346 * PARTITION BY clause and we're the top-level WindowAgg then
2347 * we're done. This tuple and any future tuples cannot
2348 * possibly match the runcondition. However, when there is a
2349 * PARTITION BY clause or we're not the top-level window we
2350 * can't just stop as we need to either process other
2351 * partitions or ensure WindowAgg nodes above us receive all
2352 * of the tuples they need to process their WindowFuncs.
2353 */
2354 if (winstate->use_pass_through)
2355 {
2356 /*
2357 * When switching into a pass-through mode, we'd better
2358 * NULLify the aggregate results as these are no longer
2359 * updated and NULLifying them avoids the old stale
2360 * results lingering. Some of these might be byref types
2361 * so we can't have them pointing to free'd memory. The
2362 * planner insisted that quals used in the runcondition
2363 * are strict, so the top-level WindowAgg will always
2364 * filter these NULLs out in the filter clause.
2365 */
2366 numfuncs = winstate->numfuncs;
2367 for (i = 0; i < numfuncs; i++)
2368 {
2369 econtext->ecxt_aggvalues[i] = (Datum) 0;
2370 econtext->ecxt_aggnulls[i] = true;
2371 }
2372
2373 /*
2374 * STRICT pass-through mode is required for the top window
2375 * when there is a PARTITION BY clause. Otherwise we must
2376 * ensure we store tuples that don't match the
2377 * runcondition so they're available to WindowAggs above.
2378 */
2379 if (winstate->top_window)
2380 {
2382 continue;
2383 }
2384 else
2385 {
2386 winstate->status = WINDOWAGG_PASSTHROUGH;
2387 }
2388 }
2389 else
2390 {
2391 /*
2392 * Pass-through not required. We can just return NULL.
2393 * Nothing else will match the runcondition.
2394 */
2395 winstate->status = WINDOWAGG_DONE;
2396 return NULL;
2397 }
2398 }
2399
2400 /*
2401 * Filter out any tuples we don't need in the top-level WindowAgg.
2402 */
2403 if (!ExecQual(winstate->ss.ps.qual, econtext))
2404 {
2405 InstrCountFiltered1(winstate, 1);
2406 continue;
2407 }
2408
2409 break;
2410 }
2411
2412 /*
2413 * When not in WINDOWAGG_RUN mode, we must still return this tuple if
2414 * we're anything apart from the top window.
2415 */
2416 else if (!winstate->top_window)
2417 break;
2418 }
2419
2420 return slot;
2421}
#define InstrCountFiltered1(node, delta)
Definition: execnodes.h:1230
@ WINDOWAGG_PASSTHROUGH
Definition: execnodes.h:2583
@ WINDOWAGG_DONE
Definition: execnodes.h:2581
@ WINDOWAGG_PASSTHROUGH_STRICT
Definition: execnodes.h:2584
static TupleTableSlot * ExecProject(ProjectionInfo *projInfo)
Definition: executor.h:389
static bool ExecQual(ExprState *state, ExprContext *econtext)
Definition: executor.h:426
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
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:600
#define FRAMEOPTION_EXCLUDE_GROUP
Definition: parsenodes.h:599
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:258
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1166
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1130
void tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
Definition: tuplestore.c:507
void tuplestore_trim(Tuplestorestate *state)
Definition: tuplestore.c:1412

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

Referenced by ExecInitWindowAgg().

◆ finalize_windowaggregate()

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

Definition at line 582 of file nodeWindowAgg.c.

586{
587 MemoryContext oldContext;
588
590
591 /*
592 * Apply the agg's finalfn if one is provided, else return transValue.
593 */
594 if (OidIsValid(peraggstate->finalfn_oid))
595 {
597 int numFinalArgs = peraggstate->numFinalArgs;
598 bool anynull;
599 int i;
600
601 InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
602 numFinalArgs,
603 perfuncstate->winCollation,
604 (Node *) winstate, NULL);
605 fcinfo->args[0].value =
607 peraggstate->transValueIsNull,
608 peraggstate->transtypeLen);
609 fcinfo->args[0].isnull = peraggstate->transValueIsNull;
610 anynull = peraggstate->transValueIsNull;
611
612 /* Fill any remaining argument positions with nulls */
613 for (i = 1; i < numFinalArgs; i++)
614 {
615 fcinfo->args[i].value = (Datum) 0;
616 fcinfo->args[i].isnull = true;
617 anynull = true;
618 }
619
620 if (fcinfo->flinfo->fn_strict && anynull)
621 {
622 /* don't call a strict function with NULL inputs */
623 *result = (Datum) 0;
624 *isnull = true;
625 }
626 else
627 {
628 Datum res;
629
630 winstate->curaggcontext = peraggstate->aggcontext;
631 res = FunctionCallInvoke(fcinfo);
632 winstate->curaggcontext = NULL;
633 *isnull = fcinfo->isnull;
635 fcinfo->isnull,
636 peraggstate->resulttypeLen);
637 }
638 }
639 else
640 {
641 *result =
643 peraggstate->transValueIsNull,
644 peraggstate->transtypeLen);
645 *isnull = peraggstate->transValueIsNull;
646 }
647
648 MemoryContextSwitchTo(oldContext);
649}
#define MakeExpandedObjectReadOnly(d, isnull, typlen)

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

Referenced by eval_windowaggregates().

◆ GetAggInitVal()

static Datum GetAggInitVal ( Datum  textInitVal,
Oid  transtype 
)
static

Definition at line 3087 of file nodeWindowAgg.c.

3088{
3089 Oid typinput,
3090 typioparam;
3091 char *strInitVal;
3092 Datum initVal;
3093
3094 getTypeInputInfo(transtype, &typinput, &typioparam);
3095 strInitVal = TextDatumGetCString(textInitVal);
3096 initVal = OidInputFunctionCall(typinput, strInitVal,
3097 typioparam, -1);
3098 pfree(strInitVal);
3099 return initVal;
3100}
#define TextDatumGetCString(d)
Definition: builtins.h:98
Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
Definition: fmgr.c:1754
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition: lsyscache.c:2874
unsigned int Oid
Definition: postgres_ext.h:31

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

Referenced by initialize_peragg().

◆ initialize_peragg()

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

Definition at line 2814 of file nodeWindowAgg.c.

2816{
2817 Oid inputTypes[FUNC_MAX_ARGS];
2818 int numArguments;
2819 HeapTuple aggTuple;
2820 Form_pg_aggregate aggform;
2821 Oid aggtranstype;
2822 AttrNumber initvalAttNo;
2823 AclResult aclresult;
2824 bool use_ma_code;
2825 Oid transfn_oid,
2826 invtransfn_oid,
2827 finalfn_oid;
2828 bool finalextra;
2829 char finalmodify;
2830 Expr *transfnexpr,
2831 *invtransfnexpr,
2832 *finalfnexpr;
2833 Datum textInitVal;
2834 int i;
2835 ListCell *lc;
2836
2837 numArguments = list_length(wfunc->args);
2838
2839 i = 0;
2840 foreach(lc, wfunc->args)
2841 {
2842 inputTypes[i++] = exprType((Node *) lfirst(lc));
2843 }
2844
2845 aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
2846 if (!HeapTupleIsValid(aggTuple))
2847 elog(ERROR, "cache lookup failed for aggregate %u",
2848 wfunc->winfnoid);
2849 aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
2850
2851 /*
2852 * Figure out whether we want to use the moving-aggregate implementation,
2853 * and collect the right set of fields from the pg_aggregate entry.
2854 *
2855 * It's possible that an aggregate would supply a safe moving-aggregate
2856 * implementation and an unsafe normal one, in which case our hand is
2857 * forced. Otherwise, if the frame head can't move, we don't need
2858 * moving-aggregate code. Even if we'd like to use it, don't do so if the
2859 * aggregate's arguments (and FILTER clause if any) contain any calls to
2860 * volatile functions. Otherwise, the difference between restarting and
2861 * not restarting the aggregation would be user-visible.
2862 *
2863 * We also don't risk using moving aggregates when there are subplans in
2864 * the arguments or FILTER clause. This is partly because
2865 * contain_volatile_functions() doesn't look inside subplans; but there
2866 * are other reasons why a subplan's output might be volatile. For
2867 * example, syncscan mode can render the results nonrepeatable.
2868 */
2869 if (!OidIsValid(aggform->aggminvtransfn))
2870 use_ma_code = false; /* sine qua non */
2871 else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
2872 aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
2873 use_ma_code = true; /* decision forced by safety */
2875 use_ma_code = false; /* non-moving frame head */
2876 else if (contain_volatile_functions((Node *) wfunc))
2877 use_ma_code = false; /* avoid possible behavioral change */
2878 else if (contain_subplans((Node *) wfunc))
2879 use_ma_code = false; /* subplans might contain volatile functions */
2880 else
2881 use_ma_code = true; /* yes, let's use it */
2882 if (use_ma_code)
2883 {
2884 peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
2885 peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
2886 peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
2887 finalextra = aggform->aggmfinalextra;
2888 finalmodify = aggform->aggmfinalmodify;
2889 aggtranstype = aggform->aggmtranstype;
2890 initvalAttNo = Anum_pg_aggregate_aggminitval;
2891 }
2892 else
2893 {
2894 peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
2895 peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
2896 peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
2897 finalextra = aggform->aggfinalextra;
2898 finalmodify = aggform->aggfinalmodify;
2899 aggtranstype = aggform->aggtranstype;
2900 initvalAttNo = Anum_pg_aggregate_agginitval;
2901 }
2902
2903 /*
2904 * ExecInitWindowAgg already checked permission to call aggregate function
2905 * ... but we still need to check the component functions
2906 */
2907
2908 /* Check that aggregate owner has permission to call component fns */
2909 {
2910 HeapTuple procTuple;
2911 Oid aggOwner;
2912
2913 procTuple = SearchSysCache1(PROCOID,
2914 ObjectIdGetDatum(wfunc->winfnoid));
2915 if (!HeapTupleIsValid(procTuple))
2916 elog(ERROR, "cache lookup failed for function %u",
2917 wfunc->winfnoid);
2918 aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
2919 ReleaseSysCache(procTuple);
2920
2921 aclresult = object_aclcheck(ProcedureRelationId, transfn_oid, aggOwner,
2922 ACL_EXECUTE);
2923 if (aclresult != ACLCHECK_OK)
2925 get_func_name(transfn_oid));
2926 InvokeFunctionExecuteHook(transfn_oid);
2927
2928 if (OidIsValid(invtransfn_oid))
2929 {
2930 aclresult = object_aclcheck(ProcedureRelationId, invtransfn_oid, aggOwner,
2931 ACL_EXECUTE);
2932 if (aclresult != ACLCHECK_OK)
2934 get_func_name(invtransfn_oid));
2935 InvokeFunctionExecuteHook(invtransfn_oid);
2936 }
2937
2938 if (OidIsValid(finalfn_oid))
2939 {
2940 aclresult = object_aclcheck(ProcedureRelationId, finalfn_oid, aggOwner,
2941 ACL_EXECUTE);
2942 if (aclresult != ACLCHECK_OK)
2944 get_func_name(finalfn_oid));
2945 InvokeFunctionExecuteHook(finalfn_oid);
2946 }
2947 }
2948
2949 /*
2950 * If the selected finalfn isn't read-only, we can't run this aggregate as
2951 * a window function. This is a user-facing error, so we take a bit more
2952 * care with the error message than elsewhere in this function.
2953 */
2954 if (finalmodify != AGGMODIFY_READ_ONLY)
2955 ereport(ERROR,
2956 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2957 errmsg("aggregate function %s does not support use as a window function",
2958 format_procedure(wfunc->winfnoid))));
2959
2960 /* Detect how many arguments to pass to the finalfn */
2961 if (finalextra)
2962 peraggstate->numFinalArgs = numArguments + 1;
2963 else
2964 peraggstate->numFinalArgs = 1;
2965
2966 /* resolve actual type of transition state, if polymorphic */
2967 aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
2968 aggtranstype,
2969 inputTypes,
2970 numArguments);
2971
2972 /* build expression trees using actual argument & result types */
2974 numArguments,
2975 0, /* no ordered-set window functions yet */
2976 false, /* no variadic window functions yet */
2977 aggtranstype,
2978 wfunc->inputcollid,
2979 transfn_oid,
2980 invtransfn_oid,
2981 &transfnexpr,
2982 &invtransfnexpr);
2983
2984 /* set up infrastructure for calling the transfn(s) and finalfn */
2985 fmgr_info(transfn_oid, &peraggstate->transfn);
2986 fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
2987
2988 if (OidIsValid(invtransfn_oid))
2989 {
2990 fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
2991 fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
2992 }
2993
2994 if (OidIsValid(finalfn_oid))
2995 {
2997 peraggstate->numFinalArgs,
2998 aggtranstype,
2999 wfunc->wintype,
3000 wfunc->inputcollid,
3001 finalfn_oid,
3002 &finalfnexpr);
3003 fmgr_info(finalfn_oid, &peraggstate->finalfn);
3004 fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
3005 }
3006
3007 /* get info about relevant datatypes */
3008 get_typlenbyval(wfunc->wintype,
3009 &peraggstate->resulttypeLen,
3010 &peraggstate->resulttypeByVal);
3011 get_typlenbyval(aggtranstype,
3012 &peraggstate->transtypeLen,
3013 &peraggstate->transtypeByVal);
3014
3015 /*
3016 * initval is potentially null, so don't try to access it as a struct
3017 * field. Must do it the hard way with SysCacheGetAttr.
3018 */
3019 textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
3020 &peraggstate->initValueIsNull);
3021
3022 if (peraggstate->initValueIsNull)
3023 peraggstate->initValue = (Datum) 0;
3024 else
3025 peraggstate->initValue = GetAggInitVal(textInitVal,
3026 aggtranstype);
3027
3028 /*
3029 * If the transfn is strict and the initval is NULL, make sure input type
3030 * and transtype are the same (or at least binary-compatible), so that
3031 * it's OK to use the first input value as the initial transValue. This
3032 * should have been checked at agg definition time, but we must check
3033 * again in case the transfn's strictness property has been changed.
3034 */
3035 if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
3036 {
3037 if (numArguments < 1 ||
3038 !IsBinaryCoercible(inputTypes[0], aggtranstype))
3039 ereport(ERROR,
3040 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
3041 errmsg("aggregate %u needs to have compatible input type and transition type",
3042 wfunc->winfnoid)));
3043 }
3044
3045 /*
3046 * Insist that forward and inverse transition functions have the same
3047 * strictness setting. Allowing them to differ would require handling
3048 * more special cases in advance_windowaggregate and
3049 * advance_windowaggregate_base, for no discernible benefit. This should
3050 * have been checked at agg definition time, but we must check again in
3051 * case either function's strictness property has been changed.
3052 */
3053 if (OidIsValid(invtransfn_oid) &&
3054 peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
3055 ereport(ERROR,
3056 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
3057 errmsg("strictness of aggregate's forward and inverse transition functions must match")));
3058
3059 /*
3060 * Moving aggregates use their own aggcontext.
3061 *
3062 * This is necessary because they might restart at different times, so we
3063 * might never be able to reset the shared context otherwise. We can't
3064 * make it the aggregates' responsibility to clean up after themselves,
3065 * because strict aggregates must be restarted whenever we remove their
3066 * last non-NULL input, which the aggregate won't be aware is happening.
3067 * Also, just pfree()ing the transValue upon restarting wouldn't help,
3068 * since we'd miss any indirectly referenced data. We could, in theory,
3069 * make the memory allocation rules for moving aggregates different than
3070 * they have historically been for plain aggregates, but that seems grotty
3071 * and likely to lead to memory leaks.
3072 */
3073 if (OidIsValid(invtransfn_oid))
3074 peraggstate->aggcontext =
3076 "WindowAgg Per Aggregate",
3078 else
3079 peraggstate->aggcontext = winstate->aggcontext;
3080
3081 ReleaseSysCache(aggTuple);
3082
3083 return peraggstate;
3084}
int16 AttrNumber
Definition: attnum.h:21
bool contain_subplans(Node *clause)
Definition: clauses.c:329
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
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:2225
Oid resolve_aggregate_transtype(Oid aggfuncid, Oid aggtranstype, Oid *inputTypes, int numArguments)
Definition: parse_agg.c:2023
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:2117
bool IsBinaryCoercible(Oid srctype, Oid targettype)
#define FRAMEOPTION_START_UNBOUNDED_PRECEDING
Definition: parsenodes.h:588
FormData_pg_aggregate * Form_pg_aggregate
Definition: pg_aggregate.h:109
FormData_pg_proc * Form_pg_proc
Definition: pg_proc.h:136
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
char * format_procedure(Oid procedure_oid)
Definition: regproc.c:299
List * args
Definition: primnodes.h:575
Oid winfnoid
Definition: primnodes.h:567
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600

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

Referenced by ExecInitWindowAgg().

◆ initialize_windowaggregate()

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

Definition at line 207 of file nodeWindowAgg.c.

210{
211 MemoryContext oldContext;
212
213 /*
214 * If we're using a private aggcontext, we may reset it here. But if the
215 * context is shared, we don't know which other aggregates may still need
216 * it, so we must leave it to the caller to reset at an appropriate time.
217 */
218 if (peraggstate->aggcontext != winstate->aggcontext)
219 MemoryContextReset(peraggstate->aggcontext);
220
221 if (peraggstate->initValueIsNull)
222 peraggstate->transValue = peraggstate->initValue;
223 else
224 {
225 oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
226 peraggstate->transValue = datumCopy(peraggstate->initValue,
227 peraggstate->transtypeByVal,
228 peraggstate->transtypeLen);
229 MemoryContextSwitchTo(oldContext);
230 }
231 peraggstate->transValueIsNull = peraggstate->initValueIsNull;
232 peraggstate->transValueCount = 0;
233 peraggstate->resultValue = (Datum) 0;
234 peraggstate->resultValueIsNull = true;
235}

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

Referenced by advance_windowaggregate_base(), and eval_windowaggregates().

◆ prepare_tuplestore()

static pg_noinline void prepare_tuplestore ( WindowAggState winstate)
static

Definition at line 1085 of file nodeWindowAgg.c.

1086{
1087 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1088 int frameOptions = winstate->frameOptions;
1089 int numfuncs = winstate->numfuncs;
1090
1091 /* we shouldn't be called if this was done already */
1092 Assert(winstate->buffer == NULL);
1093
1094 /* Create new tuplestore */
1095 winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
1096
1097 /*
1098 * Set up read pointers for the tuplestore. The current pointer doesn't
1099 * need BACKWARD capability, but the per-window-function read pointers do,
1100 * and the aggregate pointer does if we might need to restart aggregation.
1101 */
1102 winstate->current_ptr = 0; /* read pointer 0 is pre-allocated */
1103
1104 /* reset default REWIND capability bit for current ptr */
1105 tuplestore_set_eflags(winstate->buffer, 0);
1106
1107 /* create read pointers for aggregates, if needed */
1108 if (winstate->numaggs > 0)
1109 {
1110 WindowObject agg_winobj = winstate->agg_winobj;
1111 int readptr_flags = 0;
1112
1113 /*
1114 * If the frame head is potentially movable, or we have an EXCLUSION
1115 * clause, we might need to restart aggregation ...
1116 */
1117 if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
1118 (frameOptions & FRAMEOPTION_EXCLUSION))
1119 {
1120 /* ... so create a mark pointer to track the frame head */
1121 agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
1122 /* and the read pointer will need BACKWARD capability */
1123 readptr_flags |= EXEC_FLAG_BACKWARD;
1124 }
1125
1126 agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1127 readptr_flags);
1128 }
1129
1130 /* create mark and read pointers for each real window function */
1131 for (int i = 0; i < numfuncs; i++)
1132 {
1133 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1134
1135 if (!perfuncstate->plain_agg)
1136 {
1137 WindowObject winobj = perfuncstate->winobj;
1138
1139 winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
1140 0);
1141 winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
1143 }
1144 }
1145
1146 /*
1147 * If we are in RANGE or GROUPS mode, then determining frame boundaries
1148 * requires physical access to the frame endpoint rows, except in certain
1149 * degenerate cases. We create read pointers to point to those rows, to
1150 * simplify access and ensure that the tuplestore doesn't discard the
1151 * endpoint rows prematurely. (Must create pointers in exactly the same
1152 * cases that update_frameheadpos and update_frametailpos need them.)
1153 */
1154 winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
1155
1156 if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1157 {
1158 if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
1159 node->ordNumCols != 0) ||
1160 (frameOptions & FRAMEOPTION_START_OFFSET))
1161 winstate->framehead_ptr =
1163 if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
1164 node->ordNumCols != 0) ||
1165 (frameOptions & FRAMEOPTION_END_OFFSET))
1166 winstate->frametail_ptr =
1168 }
1169
1170 /*
1171 * If we have an exclusion clause that requires knowing the boundaries of
1172 * the current row's peer group, we create a read pointer to track the
1173 * tail position of the peer group (i.e., first row of the next peer
1174 * group). The head position does not require its own pointer because we
1175 * maintain that as a side effect of advancing the current row.
1176 */
1177 winstate->grouptail_ptr = -1;
1178
1179 if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
1181 node->ordNumCols != 0)
1182 {
1183 winstate->grouptail_ptr =
1185 }
1186}
int work_mem
Definition: globals.c:130
int tuplestore_alloc_read_pointer(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:395
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:330
void tuplestore_set_eflags(Tuplestorestate *state, int eflags)
Definition: tuplestore.c:371

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

Referenced by begin_partition().

◆ release_partition()

static void release_partition ( WindowAggState winstate)
static

Definition at line 1376 of file nodeWindowAgg.c.

1377{
1378 int i;
1379
1380 for (i = 0; i < winstate->numfuncs; i++)
1381 {
1382 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
1383
1384 /* Release any partition-local state of this window function */
1385 if (perfuncstate->winobj)
1386 perfuncstate->winobj->localmem = NULL;
1387 }
1388
1389 /*
1390 * Release all partition-local memory (in particular, any partition-local
1391 * state that we might have trashed our pointers to in the above loop, and
1392 * any aggregate temp data). We don't rely on retail pfree because some
1393 * aggregates might have allocated data we don't have direct pointers to.
1394 */
1396 MemoryContextReset(winstate->aggcontext);
1397 for (i = 0; i < winstate->numaggs; i++)
1398 {
1399 if (winstate->peragg[i].aggcontext != winstate->aggcontext)
1401 }
1402
1403 if (winstate->buffer)
1404 tuplestore_clear(winstate->buffer);
1405 winstate->partition_spooled = false;
1406 winstate->next_partition = true;
1407}
void tuplestore_clear(Tuplestorestate *state)
Definition: tuplestore.c:430

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

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

◆ row_is_in_frame()

static int row_is_in_frame ( WindowAggState winstate,
int64  pos,
TupleTableSlot slot 
)
static

Definition at line 1426 of file nodeWindowAgg.c.

1427{
1428 int frameOptions = winstate->frameOptions;
1429
1430 Assert(pos >= 0); /* else caller error */
1431
1432 /*
1433 * First, check frame starting conditions. We might as well delegate this
1434 * to update_frameheadpos always; it doesn't add any notable cost.
1435 */
1436 update_frameheadpos(winstate);
1437 if (pos < winstate->frameheadpos)
1438 return 0;
1439
1440 /*
1441 * Okay so far, now check frame ending conditions. Here, we avoid calling
1442 * update_frametailpos in simple cases, so as not to spool tuples further
1443 * ahead than necessary.
1444 */
1445 if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1446 {
1447 if (frameOptions & FRAMEOPTION_ROWS)
1448 {
1449 /* rows after current row are out of frame */
1450 if (pos > winstate->currentpos)
1451 return -1;
1452 }
1453 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1454 {
1455 /* following row that is not peer is out of frame */
1456 if (pos > winstate->currentpos &&
1457 !are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
1458 return -1;
1459 }
1460 else
1461 Assert(false);
1462 }
1463 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1464 {
1465 if (frameOptions & FRAMEOPTION_ROWS)
1466 {
1467 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1468
1469 /* rows after current row + offset are out of frame */
1470 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1471 offset = -offset;
1472
1473 if (pos > winstate->currentpos + offset)
1474 return -1;
1475 }
1476 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1477 {
1478 /* hard cases, so delegate to update_frametailpos */
1479 update_frametailpos(winstate);
1480 if (pos >= winstate->frametailpos)
1481 return -1;
1482 }
1483 else
1484 Assert(false);
1485 }
1486
1487 /* Check exclusion clause */
1488 if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
1489 {
1490 if (pos == winstate->currentpos)
1491 return 0;
1492 }
1493 else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
1494 ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
1495 pos != winstate->currentpos))
1496 {
1497 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1498
1499 /* If no ORDER BY, all rows are peers with each other */
1500 if (node->ordNumCols == 0)
1501 return 0;
1502 /* Otherwise, check the group boundaries */
1503 if (pos >= winstate->groupheadpos)
1504 {
1505 update_grouptailpos(winstate);
1506 if (pos < winstate->grouptailpos)
1507 return 0;
1508 }
1509 }
1510
1511 /* If we get here, it's in frame */
1512 return 1;
1513}
#define FRAMEOPTION_EXCLUDE_CURRENT_ROW
Definition: parsenodes.h:598
#define FRAMEOPTION_END_OFFSET_PRECEDING
Definition: parsenodes.h:595

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

Referenced by eval_windowaggregates(), and WinGetFuncArgInFrame().

◆ spool_tuples()

static void spool_tuples ( WindowAggState winstate,
int64  pos 
)
static

Definition at line 1282 of file nodeWindowAgg.c.

1283{
1284 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1286 TupleTableSlot *outerslot;
1287 MemoryContext oldcontext;
1288
1289 if (!winstate->buffer)
1290 return; /* just a safety check */
1291 if (winstate->partition_spooled)
1292 return; /* whole partition done already */
1293
1294 /*
1295 * When in pass-through mode we can just exhaust all tuples in the current
1296 * partition. We don't need these tuples for any further window function
1297 * evaluation, however, we do need to keep them around if we're not the
1298 * top-level window as another WindowAgg node above must see these.
1299 */
1300 if (winstate->status != WINDOWAGG_RUN)
1301 {
1302 Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
1304
1305 pos = -1;
1306 }
1307
1308 /*
1309 * If the tuplestore has spilled to disk, alternate reading and writing
1310 * becomes quite expensive due to frequent buffer flushes. It's cheaper
1311 * to force the entire partition to get spooled in one go.
1312 *
1313 * XXX this is a horrid kluge --- it'd be better to fix the performance
1314 * problem inside tuplestore. FIXME
1315 */
1316 else if (!tuplestore_in_memory(winstate->buffer))
1317 pos = -1;
1318
1319 outerPlan = outerPlanState(winstate);
1320
1321 /* Must be in query context to call outerplan */
1323
1324 while (winstate->spooled_rows <= pos || pos == -1)
1325 {
1326 outerslot = ExecProcNode(outerPlan);
1327 if (TupIsNull(outerslot))
1328 {
1329 /* reached the end of the last partition */
1330 winstate->partition_spooled = true;
1331 winstate->more_partitions = false;
1332 break;
1333 }
1334
1335 if (node->partNumCols > 0)
1336 {
1337 ExprContext *econtext = winstate->tmpcontext;
1338
1339 econtext->ecxt_innertuple = winstate->first_part_slot;
1340 econtext->ecxt_outertuple = outerslot;
1341
1342 /* Check if this tuple still belongs to the current partition */
1343 if (!ExecQualAndReset(winstate->partEqfunction, econtext))
1344 {
1345 /*
1346 * end of partition; copy the tuple for the next cycle.
1347 */
1348 ExecCopySlot(winstate->first_part_slot, outerslot);
1349 winstate->partition_spooled = true;
1350 winstate->more_partitions = true;
1351 break;
1352 }
1353 }
1354
1355 /*
1356 * Remember the tuple unless we're the top-level window and we're in
1357 * pass-through mode.
1358 */
1359 if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
1360 {
1361 /* Still in partition, so save it into the tuplestore */
1362 tuplestore_puttupleslot(winstate->buffer, outerslot);
1363 winstate->spooled_rows++;
1364 }
1365 }
1366
1367 MemoryContextSwitchTo(oldcontext);
1368}
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:260
bool tuplestore_in_memory(Tuplestorestate *state)
Definition: tuplestore.c:1554

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

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

◆ update_frameheadpos()

static void update_frameheadpos ( WindowAggState winstate)
static

Definition at line 1526 of file nodeWindowAgg.c.

1527{
1528 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1529 int frameOptions = winstate->frameOptions;
1530 MemoryContext oldcontext;
1531
1532 if (winstate->framehead_valid)
1533 return; /* already known for current row */
1534
1535 /* We may be called in a short-lived context */
1537
1538 if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
1539 {
1540 /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
1541 winstate->frameheadpos = 0;
1542 winstate->framehead_valid = true;
1543 }
1544 else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
1545 {
1546 if (frameOptions & FRAMEOPTION_ROWS)
1547 {
1548 /* In ROWS mode, frame head is the same as current */
1549 winstate->frameheadpos = winstate->currentpos;
1550 winstate->framehead_valid = true;
1551 }
1552 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1553 {
1554 /* If no ORDER BY, all rows are peers with each other */
1555 if (node->ordNumCols == 0)
1556 {
1557 winstate->frameheadpos = 0;
1558 winstate->framehead_valid = true;
1559 MemoryContextSwitchTo(oldcontext);
1560 return;
1561 }
1562
1563 /*
1564 * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
1565 * first row that is a peer of current row. We keep a copy of the
1566 * last-known frame head row in framehead_slot, and advance as
1567 * necessary. Note that if we reach end of partition, we will
1568 * leave frameheadpos = end+1 and framehead_slot empty.
1569 */
1571 winstate->framehead_ptr);
1572 if (winstate->frameheadpos == 0 &&
1573 TupIsNull(winstate->framehead_slot))
1574 {
1575 /* fetch first row into framehead_slot, if we didn't already */
1576 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1577 winstate->framehead_slot))
1578 elog(ERROR, "unexpected end of tuplestore");
1579 }
1580
1581 while (!TupIsNull(winstate->framehead_slot))
1582 {
1583 if (are_peers(winstate, winstate->framehead_slot,
1584 winstate->ss.ss_ScanTupleSlot))
1585 break; /* this row is the correct frame head */
1586 /* Note we advance frameheadpos even if the fetch fails */
1587 winstate->frameheadpos++;
1588 spool_tuples(winstate, winstate->frameheadpos);
1589 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1590 winstate->framehead_slot))
1591 break; /* end of partition */
1592 }
1593 winstate->framehead_valid = true;
1594 }
1595 else
1596 Assert(false);
1597 }
1598 else if (frameOptions & FRAMEOPTION_START_OFFSET)
1599 {
1600 if (frameOptions & FRAMEOPTION_ROWS)
1601 {
1602 /* In ROWS mode, bound is physically n before/after current */
1603 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1604
1605 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1606 offset = -offset;
1607
1608 winstate->frameheadpos = winstate->currentpos + offset;
1609 /* frame head can't go before first row */
1610 if (winstate->frameheadpos < 0)
1611 winstate->frameheadpos = 0;
1612 else if (winstate->frameheadpos > winstate->currentpos + 1)
1613 {
1614 /* make sure frameheadpos is not past end of partition */
1615 spool_tuples(winstate, winstate->frameheadpos - 1);
1616 if (winstate->frameheadpos > winstate->spooled_rows)
1617 winstate->frameheadpos = winstate->spooled_rows;
1618 }
1619 winstate->framehead_valid = true;
1620 }
1621 else if (frameOptions & FRAMEOPTION_RANGE)
1622 {
1623 /*
1624 * In RANGE START_OFFSET mode, frame head is the first row that
1625 * satisfies the in_range constraint relative to the current row.
1626 * We keep a copy of the last-known frame head row in
1627 * framehead_slot, and advance as necessary. Note that if we
1628 * reach end of partition, we will leave frameheadpos = end+1 and
1629 * framehead_slot empty.
1630 */
1631 int sortCol = node->ordColIdx[0];
1632 bool sub,
1633 less;
1634
1635 /* We must have an ordering column */
1636 Assert(node->ordNumCols == 1);
1637
1638 /* Precompute flags for in_range checks */
1639 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1640 sub = true; /* subtract startOffset from current row */
1641 else
1642 sub = false; /* add it */
1643 less = false; /* normally, we want frame head >= sum */
1644 /* If sort order is descending, flip both flags */
1645 if (!winstate->inRangeAsc)
1646 {
1647 sub = !sub;
1648 less = true;
1649 }
1650
1652 winstate->framehead_ptr);
1653 if (winstate->frameheadpos == 0 &&
1654 TupIsNull(winstate->framehead_slot))
1655 {
1656 /* fetch first row into framehead_slot, if we didn't already */
1657 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1658 winstate->framehead_slot))
1659 elog(ERROR, "unexpected end of tuplestore");
1660 }
1661
1662 while (!TupIsNull(winstate->framehead_slot))
1663 {
1664 Datum headval,
1665 currval;
1666 bool headisnull,
1667 currisnull;
1668
1669 headval = slot_getattr(winstate->framehead_slot, sortCol,
1670 &headisnull);
1671 currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1672 &currisnull);
1673 if (headisnull || currisnull)
1674 {
1675 /* order of the rows depends only on nulls_first */
1676 if (winstate->inRangeNullsFirst)
1677 {
1678 /* advance head if head is null and curr is not */
1679 if (!headisnull || currisnull)
1680 break;
1681 }
1682 else
1683 {
1684 /* advance head if head is not null and curr is null */
1685 if (headisnull || !currisnull)
1686 break;
1687 }
1688 }
1689 else
1690 {
1692 winstate->inRangeColl,
1693 headval,
1694 currval,
1695 winstate->startOffsetValue,
1696 BoolGetDatum(sub),
1697 BoolGetDatum(less))))
1698 break; /* this row is the correct frame head */
1699 }
1700 /* Note we advance frameheadpos even if the fetch fails */
1701 winstate->frameheadpos++;
1702 spool_tuples(winstate, winstate->frameheadpos);
1703 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1704 winstate->framehead_slot))
1705 break; /* end of partition */
1706 }
1707 winstate->framehead_valid = true;
1708 }
1709 else if (frameOptions & FRAMEOPTION_GROUPS)
1710 {
1711 /*
1712 * In GROUPS START_OFFSET mode, frame head is the first row of the
1713 * first peer group whose number satisfies the offset constraint.
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 int64 offset = DatumGetInt64(winstate->startOffsetValue);
1720 int64 minheadgroup;
1721
1722 if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
1723 minheadgroup = winstate->currentgroup - offset;
1724 else
1725 minheadgroup = winstate->currentgroup + offset;
1726
1728 winstate->framehead_ptr);
1729 if (winstate->frameheadpos == 0 &&
1730 TupIsNull(winstate->framehead_slot))
1731 {
1732 /* fetch first row into framehead_slot, if we didn't already */
1733 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1734 winstate->framehead_slot))
1735 elog(ERROR, "unexpected end of tuplestore");
1736 }
1737
1738 while (!TupIsNull(winstate->framehead_slot))
1739 {
1740 if (winstate->frameheadgroup >= minheadgroup)
1741 break; /* this row is the correct frame head */
1742 ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
1743 /* Note we advance frameheadpos even if the fetch fails */
1744 winstate->frameheadpos++;
1745 spool_tuples(winstate, winstate->frameheadpos);
1746 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1747 winstate->framehead_slot))
1748 break; /* end of partition */
1749 if (!are_peers(winstate, winstate->temp_slot_2,
1750 winstate->framehead_slot))
1751 winstate->frameheadgroup++;
1752 }
1753 ExecClearTuple(winstate->temp_slot_2);
1754 winstate->framehead_valid = true;
1755 }
1756 else
1757 Assert(false);
1758 }
1759 else
1760 Assert(false);
1761
1762 MemoryContextSwitchTo(oldcontext);
1763}
Datum FunctionCall5Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2, Datum arg3, Datum arg4, Datum arg5)
Definition: fmgr.c:1223
#define FRAMEOPTION_START_OFFSET_PRECEDING
Definition: parsenodes.h:594
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:395

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

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

◆ update_frametailpos()

static void update_frametailpos ( WindowAggState winstate)
static

Definition at line 1776 of file nodeWindowAgg.c.

1777{
1778 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
1779 int frameOptions = winstate->frameOptions;
1780 MemoryContext oldcontext;
1781
1782 if (winstate->frametail_valid)
1783 return; /* already known for current row */
1784
1785 /* We may be called in a short-lived context */
1787
1788 if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
1789 {
1790 /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
1791 spool_tuples(winstate, -1);
1792 winstate->frametailpos = winstate->spooled_rows;
1793 winstate->frametail_valid = true;
1794 }
1795 else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
1796 {
1797 if (frameOptions & FRAMEOPTION_ROWS)
1798 {
1799 /* In ROWS mode, exactly the rows up to current are in frame */
1800 winstate->frametailpos = winstate->currentpos + 1;
1801 winstate->frametail_valid = true;
1802 }
1803 else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
1804 {
1805 /* If no ORDER BY, all rows are peers with each other */
1806 if (node->ordNumCols == 0)
1807 {
1808 spool_tuples(winstate, -1);
1809 winstate->frametailpos = winstate->spooled_rows;
1810 winstate->frametail_valid = true;
1811 MemoryContextSwitchTo(oldcontext);
1812 return;
1813 }
1814
1815 /*
1816 * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
1817 * row that is a peer of current row, frame tail is the row after
1818 * that (if any). We keep a copy of the last-known frame tail row
1819 * in frametail_slot, and advance as necessary. Note that if we
1820 * reach end of partition, we will leave frametailpos = end+1 and
1821 * frametail_slot empty.
1822 */
1824 winstate->frametail_ptr);
1825 if (winstate->frametailpos == 0 &&
1826 TupIsNull(winstate->frametail_slot))
1827 {
1828 /* fetch first row into frametail_slot, if we didn't already */
1829 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1830 winstate->frametail_slot))
1831 elog(ERROR, "unexpected end of tuplestore");
1832 }
1833
1834 while (!TupIsNull(winstate->frametail_slot))
1835 {
1836 if (winstate->frametailpos > winstate->currentpos &&
1837 !are_peers(winstate, winstate->frametail_slot,
1838 winstate->ss.ss_ScanTupleSlot))
1839 break; /* this row is the frame tail */
1840 /* Note we advance frametailpos even if the fetch fails */
1841 winstate->frametailpos++;
1842 spool_tuples(winstate, winstate->frametailpos);
1843 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1844 winstate->frametail_slot))
1845 break; /* end of partition */
1846 }
1847 winstate->frametail_valid = true;
1848 }
1849 else
1850 Assert(false);
1851 }
1852 else if (frameOptions & FRAMEOPTION_END_OFFSET)
1853 {
1854 if (frameOptions & FRAMEOPTION_ROWS)
1855 {
1856 /* In ROWS mode, bound is physically n before/after current */
1857 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1858
1859 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1860 offset = -offset;
1861
1862 winstate->frametailpos = winstate->currentpos + offset + 1;
1863 /* smallest allowable value of frametailpos is 0 */
1864 if (winstate->frametailpos < 0)
1865 winstate->frametailpos = 0;
1866 else if (winstate->frametailpos > winstate->currentpos + 1)
1867 {
1868 /* make sure frametailpos is not past end of partition */
1869 spool_tuples(winstate, winstate->frametailpos - 1);
1870 if (winstate->frametailpos > winstate->spooled_rows)
1871 winstate->frametailpos = winstate->spooled_rows;
1872 }
1873 winstate->frametail_valid = true;
1874 }
1875 else if (frameOptions & FRAMEOPTION_RANGE)
1876 {
1877 /*
1878 * In RANGE END_OFFSET mode, frame end is the last row that
1879 * satisfies the in_range constraint relative to the current row,
1880 * frame tail is the row after that (if any). We keep a copy of
1881 * the last-known frame tail row in frametail_slot, and advance as
1882 * necessary. Note that if we reach end of partition, we will
1883 * leave frametailpos = end+1 and frametail_slot empty.
1884 */
1885 int sortCol = node->ordColIdx[0];
1886 bool sub,
1887 less;
1888
1889 /* We must have an ordering column */
1890 Assert(node->ordNumCols == 1);
1891
1892 /* Precompute flags for in_range checks */
1893 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1894 sub = true; /* subtract endOffset from current row */
1895 else
1896 sub = false; /* add it */
1897 less = true; /* normally, we want frame tail <= sum */
1898 /* If sort order is descending, flip both flags */
1899 if (!winstate->inRangeAsc)
1900 {
1901 sub = !sub;
1902 less = false;
1903 }
1904
1906 winstate->frametail_ptr);
1907 if (winstate->frametailpos == 0 &&
1908 TupIsNull(winstate->frametail_slot))
1909 {
1910 /* fetch first row into frametail_slot, if we didn't already */
1911 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1912 winstate->frametail_slot))
1913 elog(ERROR, "unexpected end of tuplestore");
1914 }
1915
1916 while (!TupIsNull(winstate->frametail_slot))
1917 {
1918 Datum tailval,
1919 currval;
1920 bool tailisnull,
1921 currisnull;
1922
1923 tailval = slot_getattr(winstate->frametail_slot, sortCol,
1924 &tailisnull);
1925 currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
1926 &currisnull);
1927 if (tailisnull || currisnull)
1928 {
1929 /* order of the rows depends only on nulls_first */
1930 if (winstate->inRangeNullsFirst)
1931 {
1932 /* advance tail if tail is null or curr is not */
1933 if (!tailisnull)
1934 break;
1935 }
1936 else
1937 {
1938 /* advance tail if tail is not null or curr is null */
1939 if (!currisnull)
1940 break;
1941 }
1942 }
1943 else
1944 {
1946 winstate->inRangeColl,
1947 tailval,
1948 currval,
1949 winstate->endOffsetValue,
1950 BoolGetDatum(sub),
1951 BoolGetDatum(less))))
1952 break; /* this row is the correct frame tail */
1953 }
1954 /* Note we advance frametailpos even if the fetch fails */
1955 winstate->frametailpos++;
1956 spool_tuples(winstate, winstate->frametailpos);
1957 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1958 winstate->frametail_slot))
1959 break; /* end of partition */
1960 }
1961 winstate->frametail_valid = true;
1962 }
1963 else if (frameOptions & FRAMEOPTION_GROUPS)
1964 {
1965 /*
1966 * In GROUPS END_OFFSET mode, frame end is the last row of the
1967 * last peer group whose number satisfies the offset constraint,
1968 * and frame tail is the row after that (if any). We keep a copy
1969 * of the last-known frame tail row in frametail_slot, and advance
1970 * as necessary. Note that if we reach end of partition, we will
1971 * leave frametailpos = end+1 and frametail_slot empty.
1972 */
1973 int64 offset = DatumGetInt64(winstate->endOffsetValue);
1974 int64 maxtailgroup;
1975
1976 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
1977 maxtailgroup = winstate->currentgroup - offset;
1978 else
1979 maxtailgroup = winstate->currentgroup + offset;
1980
1982 winstate->frametail_ptr);
1983 if (winstate->frametailpos == 0 &&
1984 TupIsNull(winstate->frametail_slot))
1985 {
1986 /* fetch first row into frametail_slot, if we didn't already */
1987 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
1988 winstate->frametail_slot))
1989 elog(ERROR, "unexpected end of tuplestore");
1990 }
1991
1992 while (!TupIsNull(winstate->frametail_slot))
1993 {
1994 if (winstate->frametailgroup > maxtailgroup)
1995 break; /* this row is the correct frame tail */
1996 ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
1997 /* Note we advance frametailpos even if the fetch fails */
1998 winstate->frametailpos++;
1999 spool_tuples(winstate, winstate->frametailpos);
2000 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2001 winstate->frametail_slot))
2002 break; /* end of partition */
2003 if (!are_peers(winstate, winstate->temp_slot_2,
2004 winstate->frametail_slot))
2005 winstate->frametailgroup++;
2006 }
2007 ExecClearTuple(winstate->temp_slot_2);
2008 winstate->frametail_valid = true;
2009 }
2010 else
2011 Assert(false);
2012 }
2013 else
2014 Assert(false);
2015
2016 MemoryContextSwitchTo(oldcontext);
2017}

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

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

◆ update_grouptailpos()

static void update_grouptailpos ( WindowAggState winstate)
static

Definition at line 2026 of file nodeWindowAgg.c.

2027{
2028 WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
2029 MemoryContext oldcontext;
2030
2031 if (winstate->grouptail_valid)
2032 return; /* already known for current row */
2033
2034 /* We may be called in a short-lived context */
2036
2037 /* If no ORDER BY, all rows are peers with each other */
2038 if (node->ordNumCols == 0)
2039 {
2040 spool_tuples(winstate, -1);
2041 winstate->grouptailpos = winstate->spooled_rows;
2042 winstate->grouptail_valid = true;
2043 MemoryContextSwitchTo(oldcontext);
2044 return;
2045 }
2046
2047 /*
2048 * Because grouptail_valid is reset only when current row advances into a
2049 * new peer group, we always reach here knowing that grouptailpos needs to
2050 * be advanced by at least one row. Hence, unlike the otherwise similar
2051 * case for frame tail tracking, we do not need persistent storage of the
2052 * group tail row.
2053 */
2054 Assert(winstate->grouptailpos <= winstate->currentpos);
2056 winstate->grouptail_ptr);
2057 for (;;)
2058 {
2059 /* Note we advance grouptailpos even if the fetch fails */
2060 winstate->grouptailpos++;
2061 spool_tuples(winstate, winstate->grouptailpos);
2062 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
2063 winstate->temp_slot_2))
2064 break; /* end of partition */
2065 if (winstate->grouptailpos > winstate->currentpos &&
2066 !are_peers(winstate, winstate->temp_slot_2,
2067 winstate->ss.ss_ScanTupleSlot))
2068 break; /* this row is the group tail */
2069 }
2070 ExecClearTuple(winstate->temp_slot_2);
2071 winstate->grouptail_valid = true;
2072
2073 MemoryContextSwitchTo(oldcontext);
2074}

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

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

◆ window_gettupleslot()

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

Definition at line 3132 of file nodeWindowAgg.c.

3133{
3134 WindowAggState *winstate = winobj->winstate;
3135 MemoryContext oldcontext;
3136
3137 /* often called repeatedly in a row */
3139
3140 /* Don't allow passing -1 to spool_tuples here */
3141 if (pos < 0)
3142 return false;
3143
3144 /* If necessary, fetch the tuple into the spool */
3145 spool_tuples(winstate, pos);
3146
3147 if (pos >= winstate->spooled_rows)
3148 return false;
3149
3150 if (pos < winobj->markpos)
3151 elog(ERROR, "cannot fetch row before WindowObject's mark position");
3152
3154
3155 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3156
3157 /*
3158 * Advance or rewind until we are within one tuple of the one we want.
3159 */
3160 if (winobj->seekpos < pos - 1)
3161 {
3162 if (!tuplestore_skiptuples(winstate->buffer,
3163 pos - 1 - winobj->seekpos,
3164 true))
3165 elog(ERROR, "unexpected end of tuplestore");
3166 winobj->seekpos = pos - 1;
3167 }
3168 else if (winobj->seekpos > pos + 1)
3169 {
3170 if (!tuplestore_skiptuples(winstate->buffer,
3171 winobj->seekpos - (pos + 1),
3172 false))
3173 elog(ERROR, "unexpected end of tuplestore");
3174 winobj->seekpos = pos + 1;
3175 }
3176 else if (winobj->seekpos == pos)
3177 {
3178 /*
3179 * There's no API to refetch the tuple at the current position. We
3180 * have to move one tuple forward, and then one backward. (We don't
3181 * do it the other way because we might try to fetch the row before
3182 * our mark, which isn't allowed.) XXX this case could stand to be
3183 * optimized.
3184 */
3185 tuplestore_advance(winstate->buffer, true);
3186 winobj->seekpos++;
3187 }
3188
3189 /*
3190 * Now we should be on the tuple immediately before or after the one we
3191 * want, so just fetch forwards or backwards as appropriate.
3192 *
3193 * Notice that we tell tuplestore_gettupleslot to make a physical copy of
3194 * the fetched tuple. This ensures that the slot's contents remain valid
3195 * through manipulations of the tuplestore, which some callers depend on.
3196 */
3197 if (winobj->seekpos > pos)
3198 {
3199 if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
3200 elog(ERROR, "unexpected end of tuplestore");
3201 winobj->seekpos--;
3202 }
3203 else
3204 {
3205 if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
3206 elog(ERROR, "unexpected end of tuplestore");
3207 winobj->seekpos++;
3208 }
3209
3210 Assert(winobj->seekpos == pos);
3211
3212 MemoryContextSwitchTo(oldcontext);
3213
3214 return true;
3215}
bool tuplestore_advance(Tuplestorestate *state, bool forward)
Definition: tuplestore.c:1162
bool tuplestore_skiptuples(Tuplestorestate *state, int64 ntuples, bool forward)
Definition: tuplestore.c:1187

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

Referenced by eval_windowaggregates(), WinGetFuncArgInFrame(), WinGetFuncArgInPartition(), and WinRowsArePeers().

◆ WinGetCurrentPosition()

int64 WinGetCurrentPosition ( WindowObject  winobj)

Definition at line 3251 of file nodeWindowAgg.c.

3252{
3253 Assert(WindowObjectIsValid(winobj));
3254 return winobj->winstate->currentpos;
3255}
#define WindowObjectIsValid(winobj)
Definition: windowapi.h:41

References Assert, WindowAggState::currentpos, WindowObjectIsValid, and WindowObjectData::winstate.

Referenced by rank_up(), window_cume_dist(), window_percent_rank(), window_rank(), and window_row_number().

◆ WinGetFuncArgCurrent()

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

Definition at line 3659 of file nodeWindowAgg.c.

3660{
3661 WindowAggState *winstate;
3662 ExprContext *econtext;
3663
3664 Assert(WindowObjectIsValid(winobj));
3665 winstate = winobj->winstate;
3666
3667 econtext = winstate->ss.ps.ps_ExprContext;
3668
3669 econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
3670 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3671 econtext, isnull);
3672}
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299

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

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

◆ WinGetFuncArgInFrame()

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

Definition at line 3464 of file nodeWindowAgg.c.

3467{
3468 WindowAggState *winstate;
3469 ExprContext *econtext;
3470 TupleTableSlot *slot;
3471 int64 abs_pos;
3472 int64 mark_pos;
3473
3474 Assert(WindowObjectIsValid(winobj));
3475 winstate = winobj->winstate;
3476 econtext = winstate->ss.ps.ps_ExprContext;
3477 slot = winstate->temp_slot_1;
3478
3479 switch (seektype)
3480 {
3482 elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
3483 abs_pos = mark_pos = 0; /* keep compiler quiet */
3484 break;
3485 case WINDOW_SEEK_HEAD:
3486 /* rejecting relpos < 0 is easy and simplifies code below */
3487 if (relpos < 0)
3488 goto out_of_frame;
3489 update_frameheadpos(winstate);
3490 abs_pos = winstate->frameheadpos + relpos;
3491 mark_pos = abs_pos;
3492
3493 /*
3494 * Account for exclusion option if one is active, but advance only
3495 * abs_pos not mark_pos. This prevents changes of the current
3496 * row's peer group from resulting in trying to fetch a row before
3497 * some previous mark position.
3498 *
3499 * Note that in some corner cases such as current row being
3500 * outside frame, these calculations are theoretically too simple,
3501 * but it doesn't matter because we'll end up deciding the row is
3502 * out of frame. We do not attempt to avoid fetching rows past
3503 * end of frame; that would happen in some cases anyway.
3504 */
3505 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3506 {
3507 case 0:
3508 /* no adjustment needed */
3509 break;
3511 if (abs_pos >= winstate->currentpos &&
3512 winstate->currentpos >= winstate->frameheadpos)
3513 abs_pos++;
3514 break;
3516 update_grouptailpos(winstate);
3517 if (abs_pos >= winstate->groupheadpos &&
3518 winstate->grouptailpos > winstate->frameheadpos)
3519 {
3520 int64 overlapstart = Max(winstate->groupheadpos,
3521 winstate->frameheadpos);
3522
3523 abs_pos += winstate->grouptailpos - overlapstart;
3524 }
3525 break;
3527 update_grouptailpos(winstate);
3528 if (abs_pos >= winstate->groupheadpos &&
3529 winstate->grouptailpos > winstate->frameheadpos)
3530 {
3531 int64 overlapstart = Max(winstate->groupheadpos,
3532 winstate->frameheadpos);
3533
3534 if (abs_pos == overlapstart)
3535 abs_pos = winstate->currentpos;
3536 else
3537 abs_pos += winstate->grouptailpos - overlapstart - 1;
3538 }
3539 break;
3540 default:
3541 elog(ERROR, "unrecognized frame option state: 0x%x",
3542 winstate->frameOptions);
3543 break;
3544 }
3545 break;
3546 case WINDOW_SEEK_TAIL:
3547 /* rejecting relpos > 0 is easy and simplifies code below */
3548 if (relpos > 0)
3549 goto out_of_frame;
3550 update_frametailpos(winstate);
3551 abs_pos = winstate->frametailpos - 1 + relpos;
3552
3553 /*
3554 * Account for exclusion option if one is active. If there is no
3555 * exclusion, we can safely set the mark at the accessed row. But
3556 * if there is, we can only mark the frame start, because we can't
3557 * be sure how far back in the frame the exclusion might cause us
3558 * to fetch in future. Furthermore, we have to actually check
3559 * against frameheadpos here, since it's unsafe to try to fetch a
3560 * row before frame start if the mark might be there already.
3561 */
3562 switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
3563 {
3564 case 0:
3565 /* no adjustment needed */
3566 mark_pos = abs_pos;
3567 break;
3569 if (abs_pos <= winstate->currentpos &&
3570 winstate->currentpos < winstate->frametailpos)
3571 abs_pos--;
3572 update_frameheadpos(winstate);
3573 if (abs_pos < winstate->frameheadpos)
3574 goto out_of_frame;
3575 mark_pos = winstate->frameheadpos;
3576 break;
3578 update_grouptailpos(winstate);
3579 if (abs_pos < winstate->grouptailpos &&
3580 winstate->groupheadpos < winstate->frametailpos)
3581 {
3582 int64 overlapend = Min(winstate->grouptailpos,
3583 winstate->frametailpos);
3584
3585 abs_pos -= overlapend - winstate->groupheadpos;
3586 }
3587 update_frameheadpos(winstate);
3588 if (abs_pos < winstate->frameheadpos)
3589 goto out_of_frame;
3590 mark_pos = winstate->frameheadpos;
3591 break;
3593 update_grouptailpos(winstate);
3594 if (abs_pos < winstate->grouptailpos &&
3595 winstate->groupheadpos < winstate->frametailpos)
3596 {
3597 int64 overlapend = Min(winstate->grouptailpos,
3598 winstate->frametailpos);
3599
3600 if (abs_pos == overlapend - 1)
3601 abs_pos = winstate->currentpos;
3602 else
3603 abs_pos -= overlapend - 1 - winstate->groupheadpos;
3604 }
3605 update_frameheadpos(winstate);
3606 if (abs_pos < winstate->frameheadpos)
3607 goto out_of_frame;
3608 mark_pos = winstate->frameheadpos;
3609 break;
3610 default:
3611 elog(ERROR, "unrecognized frame option state: 0x%x",
3612 winstate->frameOptions);
3613 mark_pos = 0; /* keep compiler quiet */
3614 break;
3615 }
3616 break;
3617 default:
3618 elog(ERROR, "unrecognized window seek type: %d", seektype);
3619 abs_pos = mark_pos = 0; /* keep compiler quiet */
3620 break;
3621 }
3622
3623 if (!window_gettupleslot(winobj, abs_pos, slot))
3624 goto out_of_frame;
3625
3626 /* The code above does not detect all out-of-frame cases, so check */
3627 if (row_is_in_frame(winstate, abs_pos, slot) <= 0)
3628 goto out_of_frame;
3629
3630 if (isout)
3631 *isout = false;
3632 if (set_mark)
3633 WinSetMarkPosition(winobj, mark_pos);
3634 econtext->ecxt_outertuple = slot;
3635 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3636 econtext, isnull);
3637
3638out_of_frame:
3639 if (isout)
3640 *isout = true;
3641 *isnull = true;
3642 return (Datum) 0;
3643}
#define Min(x, y)
Definition: c.h:958
#define Max(x, y)
Definition: c.h:952
#define WINDOW_SEEK_TAIL
Definition: windowapi.h:34
#define WINDOW_SEEK_HEAD
Definition: windowapi.h:33
#define WINDOW_SEEK_CURRENT
Definition: windowapi.h:32

References WindowObjectData::argstates, Assert, WindowAggState::currentpos, ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), WindowAggState::frameheadpos, FRAMEOPTION_EXCLUDE_CURRENT_ROW, FRAMEOPTION_EXCLUDE_GROUP, FRAMEOPTION_EXCLUDE_TIES, FRAMEOPTION_EXCLUSION, WindowAggState::frameOptions, WindowAggState::frametailpos, WindowAggState::groupheadpos, WindowAggState::grouptailpos, 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 3376 of file nodeWindowAgg.c.

3379{
3380 WindowAggState *winstate;
3381 ExprContext *econtext;
3382 TupleTableSlot *slot;
3383 bool gottuple;
3384 int64 abs_pos;
3385
3386 Assert(WindowObjectIsValid(winobj));
3387 winstate = winobj->winstate;
3388 econtext = winstate->ss.ps.ps_ExprContext;
3389 slot = winstate->temp_slot_1;
3390
3391 switch (seektype)
3392 {
3394 abs_pos = winstate->currentpos + relpos;
3395 break;
3396 case WINDOW_SEEK_HEAD:
3397 abs_pos = relpos;
3398 break;
3399 case WINDOW_SEEK_TAIL:
3400 spool_tuples(winstate, -1);
3401 abs_pos = winstate->spooled_rows - 1 + relpos;
3402 break;
3403 default:
3404 elog(ERROR, "unrecognized window seek type: %d", seektype);
3405 abs_pos = 0; /* keep compiler quiet */
3406 break;
3407 }
3408
3409 gottuple = window_gettupleslot(winobj, abs_pos, slot);
3410
3411 if (!gottuple)
3412 {
3413 if (isout)
3414 *isout = true;
3415 *isnull = true;
3416 return (Datum) 0;
3417 }
3418 else
3419 {
3420 if (isout)
3421 *isout = false;
3422 if (set_mark)
3423 WinSetMarkPosition(winobj, abs_pos);
3424 econtext->ecxt_outertuple = slot;
3425 return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
3426 econtext, isnull);
3427 }
3428}

References WindowObjectData::argstates, Assert, WindowAggState::currentpos, ExprContext::ecxt_outertuple, elog, ERROR, ExecEvalExpr(), list_nth(), ScanState::ps, PlanState::ps_ExprContext, spool_tuples(), WindowAggState::spooled_rows, WindowAggState::ss, WindowAggState::temp_slot_1, window_gettupleslot(), 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 3236 of file nodeWindowAgg.c.

3237{
3238 Assert(WindowObjectIsValid(winobj));
3239 if (winobj->localmem == NULL)
3240 winobj->localmem =
3242 return winobj->localmem;
3243}
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1215

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

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

◆ WinGetPartitionRowCount()

int64 WinGetPartitionRowCount ( WindowObject  winobj)

Definition at line 3266 of file nodeWindowAgg.c.

3267{
3268 Assert(WindowObjectIsValid(winobj));
3269 spool_tuples(winobj->winstate, -1);
3270 return winobj->winstate->spooled_rows;
3271}

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

3320{
3321 WindowAggState *winstate;
3322 WindowAgg *node;
3323 TupleTableSlot *slot1;
3324 TupleTableSlot *slot2;
3325 bool res;
3326
3327 Assert(WindowObjectIsValid(winobj));
3328 winstate = winobj->winstate;
3329 node = (WindowAgg *) winstate->ss.ps.plan;
3330
3331 /* If no ORDER BY, all rows are peers; don't bother to fetch them */
3332 if (node->ordNumCols == 0)
3333 return true;
3334
3335 /*
3336 * Note: OK to use temp_slot_2 here because we aren't calling any
3337 * frame-related functions (those tend to clobber temp_slot_2).
3338 */
3339 slot1 = winstate->temp_slot_1;
3340 slot2 = winstate->temp_slot_2;
3341
3342 if (!window_gettupleslot(winobj, pos1, slot1))
3343 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3344 pos1);
3345 if (!window_gettupleslot(winobj, pos2, slot2))
3346 elog(ERROR, "specified position is out of window: " INT64_FORMAT,
3347 pos2);
3348
3349 res = are_peers(winstate, slot1, slot2);
3350
3351 ExecClearTuple(slot1);
3352 ExecClearTuple(slot2);
3353
3354 return res;
3355}
#define INT64_FORMAT
Definition: c.h:503

References are_peers(), Assert, elog, ERROR, ExecClearTuple(), if(), INT64_FORMAT, WindowAgg::ordNumCols, PlanState::plan, ScanState::ps, res, 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 3284 of file nodeWindowAgg.c.

3285{
3286 WindowAggState *winstate;
3287
3288 Assert(WindowObjectIsValid(winobj));
3289 winstate = winobj->winstate;
3290
3291 if (markpos < winobj->markpos)
3292 elog(ERROR, "cannot move WindowObject's mark position backward");
3293 tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
3294 if (markpos > winobj->markpos)
3295 {
3296 tuplestore_skiptuples(winstate->buffer,
3297 markpos - winobj->markpos,
3298 true);
3299 winobj->markpos = markpos;
3300 }
3301 tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
3302 if (markpos > winobj->seekpos)
3303 {
3304 tuplestore_skiptuples(winstate->buffer,
3305 markpos - winobj->seekpos,
3306 true);
3307 winobj->seekpos = markpos;
3308 }
3309}

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

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