Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * windowfuncs.c
4 : * Standard window functions defined in SQL spec.
5 : *
6 : * Portions Copyright (c) 2000-2023, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/utils/adt/windowfuncs.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 : #include "postgres.h"
15 :
16 : #include "nodes/supportnodes.h"
17 : #include "utils/builtins.h"
18 : #include "windowapi.h"
19 :
20 : /*
21 : * ranking process information
22 : */
23 : typedef struct rank_context
24 : {
25 : int64 rank; /* current rank */
26 : } rank_context;
27 :
28 : /*
29 : * ntile process information
30 : */
31 : typedef struct
32 : {
33 : int32 ntile; /* current result */
34 : int64 rows_per_bucket; /* row number of current bucket */
35 : int64 boundary; /* how many rows should be in the bucket */
36 : int64 remainder; /* (total rows) % (bucket num) */
37 : } ntile_context;
38 :
39 : static bool rank_up(WindowObject winobj);
40 : static Datum leadlag_common(FunctionCallInfo fcinfo,
41 : bool forward, bool withoffset, bool withdefault);
42 :
43 :
44 : /*
45 : * utility routine for *_rank functions.
46 : */
47 : static bool
5215 tgl 48 CBC 82839 : rank_up(WindowObject winobj)
49 : {
50 82839 : bool up = false; /* should rank increase? */
51 82839 : int64 curpos = WinGetCurrentPosition(winobj);
52 : rank_context *context;
53 :
54 : context = (rank_context *)
55 82839 : WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
56 :
57 82839 : if (context->rank == 0)
58 : {
59 : /* first call: rank of first row is always 1 */
60 204 : Assert(curpos == 0);
61 204 : context->rank = 1;
62 : }
63 : else
64 : {
65 82635 : Assert(curpos > 0);
66 : /* do current and prior tuples match by ORDER BY clause? */
67 82635 : if (!WinRowsArePeers(winobj, curpos - 1, curpos))
68 67125 : up = true;
69 : }
70 :
71 : /* We can advance the mark, but only *after* access to prior row */
72 82839 : WinSetMarkPosition(winobj, curpos);
73 :
74 82839 : return up;
75 : }
76 :
77 :
78 : /*
79 : * row_number
80 : * just increment up from 1 until current partition finishes.
81 : */
82 : Datum
83 213828 : window_row_number(PG_FUNCTION_ARGS)
84 : {
5050 bruce 85 213828 : WindowObject winobj = PG_WINDOW_OBJECT();
5215 tgl 86 213828 : int64 curpos = WinGetCurrentPosition(winobj);
87 :
88 213828 : WinSetMarkPosition(winobj, curpos);
89 213828 : PG_RETURN_INT64(curpos + 1);
90 : }
91 :
92 : /*
93 : * window_row_number_support
94 : * prosupport function for window_row_number()
95 : */
96 : Datum
366 drowley 97 297 : window_row_number_support(PG_FUNCTION_ARGS)
98 : {
99 297 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
100 :
101 297 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
102 : {
103 27 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
104 :
105 : /* row_number() is monotonically increasing */
106 27 : req->monotonic = MONOTONICFUNC_INCREASING;
107 27 : PG_RETURN_POINTER(req);
108 : }
109 :
107 drowley 110 GNC 270 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
111 : {
112 129 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
113 :
114 : /*
115 : * The frame options can always become "ROWS BETWEEN UNBOUNDED
116 : * PRECEDING AND CURRENT ROW". row_number() always just increments by
117 : * 1 with each row in the partition. Using ROWS instead of RANGE
118 : * saves effort checking peer rows during execution.
119 : */
120 129 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
121 : FRAMEOPTION_ROWS |
122 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
123 : FRAMEOPTION_END_CURRENT_ROW);
124 :
125 129 : PG_RETURN_POINTER(req);
126 : }
127 :
366 drowley 128 CBC 141 : PG_RETURN_POINTER(NULL);
129 : }
5215 tgl 130 ECB :
131 : /*
132 : * rank
133 : * Rank changes when key columns change.
134 : * The new rank number is the current row number.
135 : */
136 : Datum
5215 tgl 137 GIC 82743 : window_rank(PG_FUNCTION_ARGS)
5215 tgl 138 ECB : {
5050 bruce 139 GIC 82743 : WindowObject winobj = PG_WINDOW_OBJECT();
140 : rank_context *context;
141 : bool up;
142 :
5215 tgl 143 CBC 82743 : up = rank_up(winobj);
144 : context = (rank_context *)
5215 tgl 145 GIC 82743 : WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
5215 tgl 146 CBC 82743 : if (up)
5215 tgl 147 GIC 67086 : context->rank = WinGetCurrentPosition(winobj) + 1;
148 :
149 82743 : PG_RETURN_INT64(context->rank);
150 : }
151 :
152 : /*
153 : * window_rank_support
154 : * prosupport function for window_rank()
366 drowley 155 ECB : */
156 : Datum
366 drowley 157 CBC 177 : window_rank_support(PG_FUNCTION_ARGS)
158 : {
366 drowley 159 GIC 177 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
160 :
366 drowley 161 CBC 177 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
162 : {
163 6 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
366 drowley 164 ECB :
165 : /* rank() is monotonically increasing */
366 drowley 166 GIC 6 : req->monotonic = MONOTONICFUNC_INCREASING;
366 drowley 167 CBC 6 : PG_RETURN_POINTER(req);
168 : }
169 :
107 drowley 170 GNC 171 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
171 : {
172 75 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
173 :
174 : /*
175 : * rank() is coded in such a way that it returns "(COUNT (*) OVER
176 : * (<opt> RANGE UNBOUNDED PRECEDING) - COUNT (*) OVER (<opt> RANGE
177 : * CURRENT ROW) + 1)" regardless of the frame options. We'll set the
178 : * frame options to "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
179 : * so they agree with what window_row_number_support() optimized the
180 : * frame options to be. Using ROWS instead of RANGE saves from doing
181 : * peer row checks during execution.
182 : */
183 75 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
184 : FRAMEOPTION_ROWS |
185 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
186 : FRAMEOPTION_END_CURRENT_ROW);
187 :
188 75 : PG_RETURN_POINTER(req);
189 : }
190 :
366 drowley 191 GIC 96 : PG_RETURN_POINTER(NULL);
192 : }
193 :
194 : /*
195 : * dense_rank
5215 tgl 196 ECB : * Rank increases by 1 when key columns change.
197 : */
198 : Datum
5215 tgl 199 GIC 36 : window_dense_rank(PG_FUNCTION_ARGS)
5215 tgl 200 ECB : {
5050 bruce 201 GIC 36 : WindowObject winobj = PG_WINDOW_OBJECT();
5050 bruce 202 ECB : rank_context *context;
203 : bool up;
204 :
5215 tgl 205 CBC 36 : up = rank_up(winobj);
5215 tgl 206 ECB : context = (rank_context *)
5215 tgl 207 GIC 36 : WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
208 36 : if (up)
5215 tgl 209 CBC 15 : context->rank++;
210 :
211 36 : PG_RETURN_INT64(context->rank);
212 : }
213 :
214 : /*
215 : * window_dense_rank_support
216 : * prosupport function for window_dense_rank()
217 : */
218 : Datum
366 drowley 219 GIC 36 : window_dense_rank_support(PG_FUNCTION_ARGS)
220 : {
221 36 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
366 drowley 222 ECB :
366 drowley 223 GIC 36 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
224 : {
225 9 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
226 :
366 drowley 227 ECB : /* dense_rank() is monotonically increasing */
366 drowley 228 GIC 9 : req->monotonic = MONOTONICFUNC_INCREASING;
229 9 : PG_RETURN_POINTER(req);
366 drowley 230 ECB : }
231 :
107 drowley 232 GNC 27 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
233 : {
234 12 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
235 :
236 : /*
237 : * dense_rank() is unaffected by the frame options. Here we set the
238 : * frame options to match what's done in row_number's support
239 : * function. Using ROWS instead of RANGE (the default) saves the
240 : * executor from having to check for peer rows.
241 : */
242 12 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
243 : FRAMEOPTION_ROWS |
244 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
245 : FRAMEOPTION_END_CURRENT_ROW);
246 :
247 12 : PG_RETURN_POINTER(req);
248 : }
249 :
366 drowley 250 GIC 15 : PG_RETURN_POINTER(NULL);
251 : }
252 :
253 : /*
254 : * percent_rank
255 : * return fraction between 0 and 1 inclusive,
5215 tgl 256 ECB : * which is described as (RK - 1) / (NR - 1), where RK is the current row's
257 : * rank and NR is the total number of rows, per spec.
258 : */
259 : Datum
5215 tgl 260 GIC 30 : window_percent_rank(PG_FUNCTION_ARGS)
261 : {
5050 bruce 262 CBC 30 : WindowObject winobj = PG_WINDOW_OBJECT();
263 : rank_context *context;
5050 bruce 264 ECB : bool up;
5050 bruce 265 CBC 30 : int64 totalrows = WinGetPartitionRowCount(winobj);
5215 tgl 266 ECB :
5215 tgl 267 GIC 30 : Assert(totalrows > 0);
5215 tgl 268 ECB :
5215 tgl 269 GIC 30 : up = rank_up(winobj);
270 : context = (rank_context *)
271 30 : WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
272 30 : if (up)
273 12 : context->rank = WinGetCurrentPosition(winobj) + 1;
274 :
275 : /* return zero if there's only one row, per spec */
5215 tgl 276 CBC 30 : if (totalrows <= 1)
5215 tgl 277 GIC 3 : PG_RETURN_FLOAT8(0.0);
5215 tgl 278 ECB :
5215 tgl 279 GIC 27 : PG_RETURN_FLOAT8((float8) (context->rank - 1) / (float8) (totalrows - 1));
5215 tgl 280 ECB : }
281 :
282 : /*
283 : * window_percent_rank_support
284 : * prosupport function for window_percent_rank()
285 : */
286 : Datum
107 drowley 287 GNC 12 : window_percent_rank_support(PG_FUNCTION_ARGS)
288 : {
289 12 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
290 :
72 291 12 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
292 : {
72 drowley 293 UNC 0 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
294 :
295 : /* percent_rank() is monotonically increasing */
296 0 : req->monotonic = MONOTONICFUNC_INCREASING;
297 0 : PG_RETURN_POINTER(req);
298 : }
299 :
107 drowley 300 GNC 12 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
301 : {
302 6 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
303 :
304 : /*
305 : * percent_rank() is unaffected by the frame options. Here we set the
306 : * frame options to match what's done in row_number's support
307 : * function. Using ROWS instead of RANGE (the default) saves the
308 : * executor from having to check for peer rows.
309 : */
310 6 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
311 : FRAMEOPTION_ROWS |
312 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
313 : FRAMEOPTION_END_CURRENT_ROW);
314 :
315 6 : PG_RETURN_POINTER(req);
316 : }
317 :
318 6 : PG_RETURN_POINTER(NULL);
319 : }
320 :
321 :
5215 tgl 322 ECB : /*
323 : * cume_dist
324 : * return fraction between 0 and 1 inclusive,
325 : * which is described as NP / NR, where NP is the number of rows preceding or
326 : * peers to the current row, and NR is the total number of rows, per spec.
327 : */
328 : Datum
5215 tgl 329 CBC 30 : window_cume_dist(PG_FUNCTION_ARGS)
330 : {
5050 bruce 331 30 : WindowObject winobj = PG_WINDOW_OBJECT();
332 : rank_context *context;
333 : bool up;
5050 bruce 334 GIC 30 : int64 totalrows = WinGetPartitionRowCount(winobj);
335 :
5215 tgl 336 30 : Assert(totalrows > 0);
337 :
338 30 : up = rank_up(winobj);
5215 tgl 339 ECB : context = (rank_context *)
5215 tgl 340 GIC 30 : WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
341 30 : if (up || context->rank == 1)
342 : {
343 : /*
5050 bruce 344 ECB : * The current row is not peer to prior row or is just the first, so
345 : * count up the number of rows that are peer to the current.
346 : */
347 : int64 row;
348 :
5215 tgl 349 GIC 24 : context->rank = WinGetCurrentPosition(winobj) + 1;
350 :
351 : /*
352 : * start from current + 1
353 : */
354 30 : for (row = context->rank; row < totalrows; row++)
355 : {
356 18 : if (!WinRowsArePeers(winobj, row - 1, row))
5215 tgl 357 CBC 12 : break;
5215 tgl 358 GIC 6 : context->rank++;
5215 tgl 359 ECB : }
360 : }
361 :
5215 tgl 362 CBC 30 : PG_RETURN_FLOAT8((float8) context->rank / (float8) totalrows);
363 : }
5215 tgl 364 ECB :
365 : /*
366 : * window_cume_dist_support
367 : * prosupport function for window_cume_dist()
368 : */
369 : Datum
107 drowley 370 GNC 12 : window_cume_dist_support(PG_FUNCTION_ARGS)
371 : {
372 12 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
373 :
72 374 12 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
375 : {
72 drowley 376 UNC 0 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
377 :
378 : /* cume_dist() is monotonically increasing */
379 0 : req->monotonic = MONOTONICFUNC_INCREASING;
380 0 : PG_RETURN_POINTER(req);
381 : }
382 :
107 drowley 383 GNC 12 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
384 : {
385 6 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
386 :
387 : /*
388 : * cume_dist() is unaffected by the frame options. Here we set the
389 : * frame options to match what's done in row_number's support
390 : * function. Using ROWS instead of RANGE (the default) saves the
391 : * executor from having to check for peer rows.
392 : */
393 6 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
394 : FRAMEOPTION_ROWS |
395 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
396 : FRAMEOPTION_END_CURRENT_ROW);
397 :
398 6 : PG_RETURN_POINTER(req);
399 : }
400 :
401 6 : PG_RETURN_POINTER(NULL);
402 : }
403 :
404 : /*
5215 tgl 405 ECB : * ntile
406 : * compute an exact numeric value with scale 0 (zero),
407 : * ranging from 1 (one) to n, per spec.
408 : */
409 : Datum
5215 tgl 410 GIC 57 : window_ntile(PG_FUNCTION_ARGS)
411 : {
5050 bruce 412 CBC 57 : WindowObject winobj = PG_WINDOW_OBJECT();
5050 bruce 413 ECB : ntile_context *context;
414 :
5215 tgl 415 : context = (ntile_context *)
5215 tgl 416 GIC 57 : WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));
417 :
418 57 : if (context->ntile == 0)
419 : {
420 : /* first call */
421 : int64 total;
422 : int32 nbuckets;
5215 tgl 423 ECB : bool isnull;
424 :
5215 tgl 425 CBC 21 : total = WinGetPartitionRowCount(winobj);
5215 tgl 426 GIC 21 : nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull));
5215 tgl 427 ECB :
428 : /*
5050 bruce 429 EUB : * per spec: If NT is the null value, then the result is the null
430 : * value.
431 : */
5215 tgl 432 GBC 21 : if (isnull)
433 6 : PG_RETURN_NULL();
434 :
435 : /*
5050 bruce 436 ECB : * per spec: If NT is less than or equal to 0 (zero), then an
437 : * exception condition is raised.
5215 tgl 438 : */
5215 tgl 439 GIC 15 : if (nbuckets <= 0)
440 3 : ereport(ERROR,
441 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE),
442 : errmsg("argument of ntile must be greater than zero")));
443 :
444 12 : context->ntile = 1;
445 12 : context->rows_per_bucket = 0;
5215 tgl 446 CBC 12 : context->boundary = total / nbuckets;
5215 tgl 447 GIC 12 : if (context->boundary <= 0)
5215 tgl 448 UIC 0 : context->boundary = 1;
449 : else
450 : {
5215 tgl 451 ECB : /*
452 : * If the total number is not divisible, add 1 row to leading
453 : * buckets.
454 : */
5215 tgl 455 GIC 12 : context->remainder = total % nbuckets;
456 12 : if (context->remainder != 0)
457 9 : context->boundary++;
458 : }
459 : }
460 :
461 48 : context->rows_per_bucket++;
462 48 : if (context->boundary < context->rows_per_bucket)
463 : {
464 : /* ntile up */
5215 tgl 465 CBC 9 : if (context->remainder != 0 && context->ntile == context->remainder)
466 : {
467 3 : context->remainder = 0;
5215 tgl 468 GIC 3 : context->boundary -= 1;
469 : }
5215 tgl 470 CBC 9 : context->ntile += 1;
5215 tgl 471 GIC 9 : context->rows_per_bucket = 1;
5215 tgl 472 ECB : }
473 :
5215 tgl 474 CBC 48 : PG_RETURN_INT32(context->ntile);
475 : }
5215 tgl 476 ECB :
477 : /*
478 : * window_ntile_support
479 : * prosupport function for window_ntile()
480 : */
481 : Datum
107 drowley 482 GNC 36 : window_ntile_support(PG_FUNCTION_ARGS)
483 : {
484 36 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
485 :
72 486 36 : if (IsA(rawreq, SupportRequestWFuncMonotonic))
487 : {
488 6 : SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
489 :
490 : /*
491 : * ntile() is monotonically increasing as the number of buckets cannot
492 : * change after the first call
493 : */
494 6 : req->monotonic = MONOTONICFUNC_INCREASING;
495 6 : PG_RETURN_POINTER(req);
496 : }
497 :
107 498 30 : if (IsA(rawreq, SupportRequestOptimizeWindowClause))
499 : {
500 12 : SupportRequestOptimizeWindowClause *req = (SupportRequestOptimizeWindowClause *) rawreq;
501 :
502 : /*
503 : * ntile() is unaffected by the frame options. Here we set the frame
504 : * options to match what's done in row_number's support function.
505 : * Using ROWS instead of RANGE (the default) saves the executor from
506 : * having to check for peer rows.
507 : */
508 12 : req->frameOptions = (FRAMEOPTION_NONDEFAULT |
509 : FRAMEOPTION_ROWS |
510 : FRAMEOPTION_START_UNBOUNDED_PRECEDING |
511 : FRAMEOPTION_END_CURRENT_ROW);
512 :
513 12 : PG_RETURN_POINTER(req);
514 : }
515 :
516 18 : PG_RETURN_POINTER(NULL);
517 : }
518 :
5215 tgl 519 ECB : /*
520 : * leadlag_common
521 : * common operation of lead() and lag()
522 : * For lead() forward is true, whereas for lag() it is false.
523 : * withoffset indicates we have an offset second argument.
524 : * withdefault indicates we have a default third argument.
525 : */
526 : static Datum
5215 tgl 527 CBC 91389 : leadlag_common(FunctionCallInfo fcinfo,
528 : bool forward, bool withoffset, bool withdefault)
529 : {
5050 bruce 530 GIC 91389 : WindowObject winobj = PG_WINDOW_OBJECT();
531 : int32 offset;
5050 bruce 532 ECB : bool const_offset;
533 : Datum result;
534 : bool isnull;
535 : bool isout;
5215 tgl 536 :
5215 tgl 537 GIC 91389 : if (withoffset)
538 : {
539 270 : offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
5215 tgl 540 CBC 270 : if (isnull)
5215 tgl 541 UIC 0 : PG_RETURN_NULL();
5215 tgl 542 GIC 270 : const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
543 : }
544 : else
545 : {
546 91119 : offset = 1;
547 91119 : const_offset = true;
5215 tgl 548 ECB : }
549 :
5215 tgl 550 CBC 91389 : result = WinGetFuncArgInPartition(winobj, 0,
551 : (forward ? offset : -offset),
5215 tgl 552 ECB : WINDOW_SEEK_CURRENT,
553 : const_offset,
5215 tgl 554 EUB : &isnull, &isout);
555 :
5215 tgl 556 GIC 91389 : if (isout)
5215 tgl 557 EUB : {
558 : /*
559 : * target row is out of the partition; supply default value if
560 : * provided. otherwise it'll stay NULL
5215 tgl 561 ECB : */
5215 tgl 562 GIC 159 : if (withdefault)
5215 tgl 563 CBC 48 : result = WinGetFuncArgCurrent(winobj, 2, &isnull);
564 : }
565 :
5215 tgl 566 GIC 91389 : if (isnull)
567 111 : PG_RETURN_NULL();
568 :
569 91278 : PG_RETURN_DATUM(result);
570 : }
5215 tgl 571 ECB :
572 : /*
573 : * lag
574 : * returns the value of VE evaluated on a row that is 1
575 : * row before the current row within a partition,
576 : * per spec.
577 : */
578 : Datum
5215 tgl 579 CBC 90969 : window_lag(PG_FUNCTION_ARGS)
580 : {
5215 tgl 581 GIC 90969 : return leadlag_common(fcinfo, false, false, false);
582 : }
583 :
584 : /*
585 : * lag_with_offset
586 : * returns the value of VE evaluated on a row that is OFFSET
587 : * rows before the current row within a partition,
5215 tgl 588 ECB : * per spec.
589 : */
590 : Datum
5215 tgl 591 GIC 60 : window_lag_with_offset(PG_FUNCTION_ARGS)
592 : {
593 60 : return leadlag_common(fcinfo, false, true, false);
5215 tgl 594 ECB : }
595 :
596 : /*
597 : * lag_with_offset_and_default
598 : * same as lag_with_offset but accepts default value
599 : * as its third argument.
600 : */
601 : Datum
5215 tgl 602 GIC 60 : window_lag_with_offset_and_default(PG_FUNCTION_ARGS)
5215 tgl 603 ECB : {
5215 tgl 604 CBC 60 : return leadlag_common(fcinfo, false, true, true);
605 : }
606 :
607 : /*
608 : * lead
609 : * returns the value of VE evaluated on a row that is 1
5215 tgl 610 ECB : * row after the current row within a partition,
611 : * per spec.
612 : */
613 : Datum
5215 tgl 614 GIC 150 : window_lead(PG_FUNCTION_ARGS)
615 : {
616 150 : return leadlag_common(fcinfo, true, false, false);
5215 tgl 617 ECB : }
618 :
619 : /*
620 : * lead_with_offset
621 : * returns the value of VE evaluated on a row that is OFFSET
622 : * number of rows after the current row within a partition,
623 : * per spec.
624 : */
625 : Datum
5215 tgl 626 GBC 90 : window_lead_with_offset(PG_FUNCTION_ARGS)
627 : {
5215 tgl 628 GIC 90 : return leadlag_common(fcinfo, true, true, false);
629 : }
630 :
631 : /*
632 : * lead_with_offset_and_default
5215 tgl 633 ECB : * same as lead_with_offset but accepts default value
634 : * as its third argument.
635 : */
636 : Datum
5215 tgl 637 GIC 60 : window_lead_with_offset_and_default(PG_FUNCTION_ARGS)
638 : {
5215 tgl 639 CBC 60 : return leadlag_common(fcinfo, true, true, true);
5215 tgl 640 ECB : }
641 :
642 : /*
643 : * first_value
644 : * return the value of VE evaluated on the first row of the
645 : * window frame, per spec.
646 : */
647 : Datum
5215 tgl 648 CBC 1233 : window_first_value(PG_FUNCTION_ARGS)
5215 tgl 649 ECB : {
5050 bruce 650 GIC 1233 : WindowObject winobj = PG_WINDOW_OBJECT();
651 : Datum result;
5050 bruce 652 ECB : bool isnull;
653 :
5215 tgl 654 GIC 1233 : result = WinGetFuncArgInFrame(winobj, 0,
655 : 0, WINDOW_SEEK_HEAD, true,
656 : &isnull, NULL);
657 1224 : if (isnull)
658 30 : PG_RETURN_NULL();
659 :
5215 tgl 660 CBC 1194 : PG_RETURN_DATUM(result);
661 : }
5215 tgl 662 ECB :
663 : /*
664 : * last_value
665 : * return the value of VE evaluated on the last row of the
666 : * window frame, per spec.
667 : */
668 : Datum
5215 tgl 669 GIC 1608 : window_last_value(PG_FUNCTION_ARGS)
670 : {
5050 bruce 671 1608 : WindowObject winobj = PG_WINDOW_OBJECT();
5050 bruce 672 ECB : Datum result;
673 : bool isnull;
674 :
5215 tgl 675 GIC 1608 : result = WinGetFuncArgInFrame(winobj, 0,
5215 tgl 676 ECB : 0, WINDOW_SEEK_TAIL, true,
677 : &isnull, NULL);
5215 tgl 678 CBC 1608 : if (isnull)
5215 tgl 679 GIC 30 : PG_RETURN_NULL();
680 :
681 1578 : PG_RETURN_DATUM(result);
682 : }
683 :
684 : /*
685 : * nth_value
5215 tgl 686 ECB : * return the value of VE evaluated on the n-th row from the first
687 : * row of the window frame, per spec.
688 : */
689 : Datum
5215 tgl 690 GIC 243 : window_nth_value(PG_FUNCTION_ARGS)
5215 tgl 691 ECB : {
5050 bruce 692 GIC 243 : WindowObject winobj = PG_WINDOW_OBJECT();
693 : bool const_offset;
5050 bruce 694 ECB : Datum result;
695 : bool isnull;
696 : int32 nth;
697 :
5215 tgl 698 GIC 243 : nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
699 243 : if (isnull)
5215 tgl 700 UIC 0 : PG_RETURN_NULL();
5215 tgl 701 GIC 243 : const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
702 :
703 243 : if (nth <= 0)
704 3 : ereport(ERROR,
5215 tgl 705 ECB : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE),
706 : errmsg("argument of nth_value must be greater than zero")));
707 :
5215 tgl 708 CBC 240 : result = WinGetFuncArgInFrame(winobj, 0,
709 : nth - 1, WINDOW_SEEK_HEAD, const_offset,
710 : &isnull, NULL);
5215 tgl 711 GIC 240 : if (isnull)
712 27 : PG_RETURN_NULL();
713 :
714 213 : PG_RETURN_DATUM(result);
5215 tgl 715 ECB : }
|