1 /*-------------------------------------------------------------------------
4 * Standard window functions defined in SQL spec.
6 * Portions Copyright (c) 2000-2024, PostgreSQL Global Development Group
10 * src/backend/utils/adt/windowfuncs.c
12 *-------------------------------------------------------------------------
16 #include "nodes/parsenodes.h"
17 #include "nodes/supportnodes.h"
18 #include "utils/fmgrprotos.h"
19 #include "windowapi.h"
22 * ranking process information
24 typedef struct rank_context
26 int64 rank
; /* current rank */
30 * ntile process information
34 int32 ntile
; /* current result */
35 int64 rows_per_bucket
; /* row number of current bucket */
36 int64 boundary
; /* how many rows should be in the bucket */
37 int64 remainder
; /* (total rows) % (bucket num) */
40 static bool rank_up(WindowObject winobj
);
41 static Datum
leadlag_common(FunctionCallInfo fcinfo
,
42 bool forward
, bool withoffset
, bool withdefault
);
46 * utility routine for *_rank functions.
49 rank_up(WindowObject winobj
)
51 bool up
= false; /* should rank increase? */
52 int64 curpos
= WinGetCurrentPosition(winobj
);
53 rank_context
*context
;
55 context
= (rank_context
*)
56 WinGetPartitionLocalMemory(winobj
, sizeof(rank_context
));
58 if (context
->rank
== 0)
60 /* first call: rank of first row is always 1 */
67 /* do current and prior tuples match by ORDER BY clause? */
68 if (!WinRowsArePeers(winobj
, curpos
- 1, curpos
))
72 /* We can advance the mark, but only *after* access to prior row */
73 WinSetMarkPosition(winobj
, curpos
);
81 * just increment up from 1 until current partition finishes.
84 window_row_number(PG_FUNCTION_ARGS
)
86 WindowObject winobj
= PG_WINDOW_OBJECT();
87 int64 curpos
= WinGetCurrentPosition(winobj
);
89 WinSetMarkPosition(winobj
, curpos
);
90 PG_RETURN_INT64(curpos
+ 1);
94 * window_row_number_support
95 * prosupport function for window_row_number()
98 window_row_number_support(PG_FUNCTION_ARGS
)
100 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
102 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
104 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
106 /* row_number() is monotonically increasing */
107 req
->monotonic
= MONOTONICFUNC_INCREASING
;
108 PG_RETURN_POINTER(req
);
111 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
113 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
116 * The frame options can always become "ROWS BETWEEN UNBOUNDED
117 * PRECEDING AND CURRENT ROW". row_number() always just increments by
118 * 1 with each row in the partition. Using ROWS instead of RANGE
119 * saves effort checking peer rows during execution.
121 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
123 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
124 FRAMEOPTION_END_CURRENT_ROW
);
126 PG_RETURN_POINTER(req
);
129 PG_RETURN_POINTER(NULL
);
134 * Rank changes when key columns change.
135 * The new rank number is the current row number.
138 window_rank(PG_FUNCTION_ARGS
)
140 WindowObject winobj
= PG_WINDOW_OBJECT();
141 rank_context
*context
;
144 up
= rank_up(winobj
);
145 context
= (rank_context
*)
146 WinGetPartitionLocalMemory(winobj
, sizeof(rank_context
));
148 context
->rank
= WinGetCurrentPosition(winobj
) + 1;
150 PG_RETURN_INT64(context
->rank
);
154 * window_rank_support
155 * prosupport function for window_rank()
158 window_rank_support(PG_FUNCTION_ARGS
)
160 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
162 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
164 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
166 /* rank() is monotonically increasing */
167 req
->monotonic
= MONOTONICFUNC_INCREASING
;
168 PG_RETURN_POINTER(req
);
171 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
173 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
176 * rank() is coded in such a way that it returns "(COUNT (*) OVER
177 * (<opt> RANGE UNBOUNDED PRECEDING) - COUNT (*) OVER (<opt> RANGE
178 * CURRENT ROW) + 1)" regardless of the frame options. We'll set the
179 * frame options to "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
180 * so they agree with what window_row_number_support() optimized the
181 * frame options to be. Using ROWS instead of RANGE saves from doing
182 * peer row checks during execution.
184 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
186 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
187 FRAMEOPTION_END_CURRENT_ROW
);
189 PG_RETURN_POINTER(req
);
192 PG_RETURN_POINTER(NULL
);
197 * Rank increases by 1 when key columns change.
200 window_dense_rank(PG_FUNCTION_ARGS
)
202 WindowObject winobj
= PG_WINDOW_OBJECT();
203 rank_context
*context
;
206 up
= rank_up(winobj
);
207 context
= (rank_context
*)
208 WinGetPartitionLocalMemory(winobj
, sizeof(rank_context
));
212 PG_RETURN_INT64(context
->rank
);
216 * window_dense_rank_support
217 * prosupport function for window_dense_rank()
220 window_dense_rank_support(PG_FUNCTION_ARGS
)
222 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
224 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
226 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
228 /* dense_rank() is monotonically increasing */
229 req
->monotonic
= MONOTONICFUNC_INCREASING
;
230 PG_RETURN_POINTER(req
);
233 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
235 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
238 * dense_rank() is unaffected by the frame options. Here we set the
239 * frame options to match what's done in row_number's support
240 * function. Using ROWS instead of RANGE (the default) saves the
241 * executor from having to check for peer rows.
243 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
245 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
246 FRAMEOPTION_END_CURRENT_ROW
);
248 PG_RETURN_POINTER(req
);
251 PG_RETURN_POINTER(NULL
);
256 * return fraction between 0 and 1 inclusive,
257 * which is described as (RK - 1) / (NR - 1), where RK is the current row's
258 * rank and NR is the total number of rows, per spec.
261 window_percent_rank(PG_FUNCTION_ARGS
)
263 WindowObject winobj
= PG_WINDOW_OBJECT();
264 rank_context
*context
;
266 int64 totalrows
= WinGetPartitionRowCount(winobj
);
268 Assert(totalrows
> 0);
270 up
= rank_up(winobj
);
271 context
= (rank_context
*)
272 WinGetPartitionLocalMemory(winobj
, sizeof(rank_context
));
274 context
->rank
= WinGetCurrentPosition(winobj
) + 1;
276 /* return zero if there's only one row, per spec */
278 PG_RETURN_FLOAT8(0.0);
280 PG_RETURN_FLOAT8((float8
) (context
->rank
- 1) / (float8
) (totalrows
- 1));
284 * window_percent_rank_support
285 * prosupport function for window_percent_rank()
288 window_percent_rank_support(PG_FUNCTION_ARGS
)
290 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
292 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
294 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
296 /* percent_rank() is monotonically increasing */
297 req
->monotonic
= MONOTONICFUNC_INCREASING
;
298 PG_RETURN_POINTER(req
);
301 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
303 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
306 * percent_rank() is unaffected by the frame options. Here we set the
307 * frame options to match what's done in row_number's support
308 * function. Using ROWS instead of RANGE (the default) saves the
309 * executor from having to check for peer rows.
311 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
313 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
314 FRAMEOPTION_END_CURRENT_ROW
);
316 PG_RETURN_POINTER(req
);
319 PG_RETURN_POINTER(NULL
);
325 * return fraction between 0 and 1 inclusive,
326 * which is described as NP / NR, where NP is the number of rows preceding or
327 * peers to the current row, and NR is the total number of rows, per spec.
330 window_cume_dist(PG_FUNCTION_ARGS
)
332 WindowObject winobj
= PG_WINDOW_OBJECT();
333 rank_context
*context
;
335 int64 totalrows
= WinGetPartitionRowCount(winobj
);
337 Assert(totalrows
> 0);
339 up
= rank_up(winobj
);
340 context
= (rank_context
*)
341 WinGetPartitionLocalMemory(winobj
, sizeof(rank_context
));
342 if (up
|| context
->rank
== 1)
345 * The current row is not peer to prior row or is just the first, so
346 * count up the number of rows that are peer to the current.
350 context
->rank
= WinGetCurrentPosition(winobj
) + 1;
353 * start from current + 1
355 for (row
= context
->rank
; row
< totalrows
; row
++)
357 if (!WinRowsArePeers(winobj
, row
- 1, row
))
363 PG_RETURN_FLOAT8((float8
) context
->rank
/ (float8
) totalrows
);
367 * window_cume_dist_support
368 * prosupport function for window_cume_dist()
371 window_cume_dist_support(PG_FUNCTION_ARGS
)
373 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
375 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
377 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
379 /* cume_dist() is monotonically increasing */
380 req
->monotonic
= MONOTONICFUNC_INCREASING
;
381 PG_RETURN_POINTER(req
);
384 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
386 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
389 * cume_dist() is unaffected by the frame options. Here we set the
390 * frame options to match what's done in row_number's support
391 * function. Using ROWS instead of RANGE (the default) saves the
392 * executor from having to check for peer rows.
394 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
396 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
397 FRAMEOPTION_END_CURRENT_ROW
);
399 PG_RETURN_POINTER(req
);
402 PG_RETURN_POINTER(NULL
);
407 * compute an exact numeric value with scale 0 (zero),
408 * ranging from 1 (one) to n, per spec.
411 window_ntile(PG_FUNCTION_ARGS
)
413 WindowObject winobj
= PG_WINDOW_OBJECT();
414 ntile_context
*context
;
416 context
= (ntile_context
*)
417 WinGetPartitionLocalMemory(winobj
, sizeof(ntile_context
));
419 if (context
->ntile
== 0)
426 total
= WinGetPartitionRowCount(winobj
);
427 nbuckets
= DatumGetInt32(WinGetFuncArgCurrent(winobj
, 0, &isnull
));
430 * per spec: If NT is the null value, then the result is the null
437 * per spec: If NT is less than or equal to 0 (zero), then an
438 * exception condition is raised.
442 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE
),
443 errmsg("argument of ntile must be greater than zero")));
446 context
->rows_per_bucket
= 0;
447 context
->boundary
= total
/ nbuckets
;
448 if (context
->boundary
<= 0)
449 context
->boundary
= 1;
453 * If the total number is not divisible, add 1 row to leading
456 context
->remainder
= total
% nbuckets
;
457 if (context
->remainder
!= 0)
462 context
->rows_per_bucket
++;
463 if (context
->boundary
< context
->rows_per_bucket
)
466 if (context
->remainder
!= 0 && context
->ntile
== context
->remainder
)
468 context
->remainder
= 0;
469 context
->boundary
-= 1;
472 context
->rows_per_bucket
= 1;
475 PG_RETURN_INT32(context
->ntile
);
479 * window_ntile_support
480 * prosupport function for window_ntile()
483 window_ntile_support(PG_FUNCTION_ARGS
)
485 Node
*rawreq
= (Node
*) PG_GETARG_POINTER(0);
487 if (IsA(rawreq
, SupportRequestWFuncMonotonic
))
489 SupportRequestWFuncMonotonic
*req
= (SupportRequestWFuncMonotonic
*) rawreq
;
492 * ntile() is monotonically increasing as the number of buckets cannot
493 * change after the first call
495 req
->monotonic
= MONOTONICFUNC_INCREASING
;
496 PG_RETURN_POINTER(req
);
499 if (IsA(rawreq
, SupportRequestOptimizeWindowClause
))
501 SupportRequestOptimizeWindowClause
*req
= (SupportRequestOptimizeWindowClause
*) rawreq
;
504 * ntile() is unaffected by the frame options. Here we set the frame
505 * options to match what's done in row_number's support function.
506 * Using ROWS instead of RANGE (the default) saves the executor from
507 * having to check for peer rows.
509 req
->frameOptions
= (FRAMEOPTION_NONDEFAULT
|
511 FRAMEOPTION_START_UNBOUNDED_PRECEDING
|
512 FRAMEOPTION_END_CURRENT_ROW
);
514 PG_RETURN_POINTER(req
);
517 PG_RETURN_POINTER(NULL
);
522 * common operation of lead() and lag()
523 * For lead() forward is true, whereas for lag() it is false.
524 * withoffset indicates we have an offset second argument.
525 * withdefault indicates we have a default third argument.
528 leadlag_common(FunctionCallInfo fcinfo
,
529 bool forward
, bool withoffset
, bool withdefault
)
531 WindowObject winobj
= PG_WINDOW_OBJECT();
540 offset
= DatumGetInt32(WinGetFuncArgCurrent(winobj
, 1, &isnull
));
543 const_offset
= get_fn_expr_arg_stable(fcinfo
->flinfo
, 1);
551 result
= WinGetFuncArgInPartition(winobj
, 0,
552 (forward
? offset
: -offset
),
560 * target row is out of the partition; supply default value if
561 * provided. otherwise it'll stay NULL
564 result
= WinGetFuncArgCurrent(winobj
, 2, &isnull
);
570 PG_RETURN_DATUM(result
);
575 * returns the value of VE evaluated on a row that is 1
576 * row before the current row within a partition,
580 window_lag(PG_FUNCTION_ARGS
)
582 return leadlag_common(fcinfo
, false, false, false);
587 * returns the value of VE evaluated on a row that is OFFSET
588 * rows before the current row within a partition,
592 window_lag_with_offset(PG_FUNCTION_ARGS
)
594 return leadlag_common(fcinfo
, false, true, false);
598 * lag_with_offset_and_default
599 * same as lag_with_offset but accepts default value
600 * as its third argument.
603 window_lag_with_offset_and_default(PG_FUNCTION_ARGS
)
605 return leadlag_common(fcinfo
, false, true, true);
610 * returns the value of VE evaluated on a row that is 1
611 * row after the current row within a partition,
615 window_lead(PG_FUNCTION_ARGS
)
617 return leadlag_common(fcinfo
, true, false, false);
622 * returns the value of VE evaluated on a row that is OFFSET
623 * number of rows after the current row within a partition,
627 window_lead_with_offset(PG_FUNCTION_ARGS
)
629 return leadlag_common(fcinfo
, true, true, false);
633 * lead_with_offset_and_default
634 * same as lead_with_offset but accepts default value
635 * as its third argument.
638 window_lead_with_offset_and_default(PG_FUNCTION_ARGS
)
640 return leadlag_common(fcinfo
, true, true, true);
645 * return the value of VE evaluated on the first row of the
646 * window frame, per spec.
649 window_first_value(PG_FUNCTION_ARGS
)
651 WindowObject winobj
= PG_WINDOW_OBJECT();
655 result
= WinGetFuncArgInFrame(winobj
, 0,
656 0, WINDOW_SEEK_HEAD
, true,
661 PG_RETURN_DATUM(result
);
666 * return the value of VE evaluated on the last row of the
667 * window frame, per spec.
670 window_last_value(PG_FUNCTION_ARGS
)
672 WindowObject winobj
= PG_WINDOW_OBJECT();
676 result
= WinGetFuncArgInFrame(winobj
, 0,
677 0, WINDOW_SEEK_TAIL
, true,
682 PG_RETURN_DATUM(result
);
687 * return the value of VE evaluated on the n-th row from the first
688 * row of the window frame, per spec.
691 window_nth_value(PG_FUNCTION_ARGS
)
693 WindowObject winobj
= PG_WINDOW_OBJECT();
699 nth
= DatumGetInt32(WinGetFuncArgCurrent(winobj
, 1, &isnull
));
702 const_offset
= get_fn_expr_arg_stable(fcinfo
->flinfo
, 1);
706 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE
),
707 errmsg("argument of nth_value must be greater than zero")));
709 result
= WinGetFuncArgInFrame(winobj
, 0,
710 nth
- 1, WINDOW_SEEK_HEAD
, const_offset
,
715 PG_RETURN_DATUM(result
);