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