1 /*-------------------------------------------------------------------------
4 * Standard window functions defined in SQL spec.
6 * Portions Copyright (c) 2000-2014, PostgreSQL Global Development Group
10 * src/backend/utils/adt/windowfuncs.c
12 *-------------------------------------------------------------------------
16 #include "utils/builtins.h"
17 #include "windowapi.h"
20 * ranking process information
22 typedef struct rank_context
24 int64 rank; /* current rank */
28 * ntile process information
32 int32 ntile; /* current result */
33 int64 rows_per_bucket; /* row number of current bucket */
34 int64 boundary; /* how many rows should be in the bucket */
35 int64 remainder; /* (total rows) % (bucket num) */
38 static bool rank_up(WindowObject winobj);
39 static Datum leadlag_common(FunctionCallInfo fcinfo,
40 bool forward, bool withoffset, bool withdefault);
44 * utility routine for *_rank functions.
47 rank_up(WindowObject winobj)
49 bool up = false; /* should rank increase? */
50 int64 curpos = WinGetCurrentPosition(winobj);
51 rank_context *context;
53 context = (rank_context *)
54 WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
56 if (context->rank == 0)
58 /* first call: rank of first row is always 1 */
65 /* do current and prior tuples match by ORDER BY clause? */
66 if (!WinRowsArePeers(winobj, curpos - 1, curpos))
70 /* We can advance the mark, but only *after* acccess to prior row */
71 WinSetMarkPosition(winobj, curpos);
79 * just increment up from 1 until current partition finishes.
82 window_row_number(PG_FUNCTION_ARGS)
84 WindowObject winobj = PG_WINDOW_OBJECT();
85 int64 curpos = WinGetCurrentPosition(winobj);
87 WinSetMarkPosition(winobj, curpos);
88 PG_RETURN_INT64(curpos + 1);
94 * Rank changes when key columns change.
95 * The new rank number is the current row number.
98 window_rank(PG_FUNCTION_ARGS)
100 WindowObject winobj = PG_WINDOW_OBJECT();
101 rank_context *context;
104 up = rank_up(winobj);
105 context = (rank_context *)
106 WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
108 context->rank = WinGetCurrentPosition(winobj) + 1;
110 PG_RETURN_INT64(context->rank);
115 * Rank increases by 1 when key columns change.
118 window_dense_rank(PG_FUNCTION_ARGS)
120 WindowObject winobj = PG_WINDOW_OBJECT();
121 rank_context *context;
124 up = rank_up(winobj);
125 context = (rank_context *)
126 WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
130 PG_RETURN_INT64(context->rank);
135 * return fraction between 0 and 1 inclusive,
136 * which is described as (RK - 1) / (NR - 1), where RK is the current row's
137 * rank and NR is the total number of rows, per spec.
140 window_percent_rank(PG_FUNCTION_ARGS)
142 WindowObject winobj = PG_WINDOW_OBJECT();
143 rank_context *context;
145 int64 totalrows = WinGetPartitionRowCount(winobj);
147 Assert(totalrows > 0);
149 up = rank_up(winobj);
150 context = (rank_context *)
151 WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
153 context->rank = WinGetCurrentPosition(winobj) + 1;
155 /* return zero if there's only one row, per spec */
157 PG_RETURN_FLOAT8(0.0);
159 PG_RETURN_FLOAT8((float8) (context->rank - 1) / (float8) (totalrows - 1));
164 * return fraction between 0 and 1 inclusive,
165 * which is described as NP / NR, where NP is the number of rows preceding or
166 * peers to the current row, and NR is the total number of rows, per spec.
169 window_cume_dist(PG_FUNCTION_ARGS)
171 WindowObject winobj = PG_WINDOW_OBJECT();
172 rank_context *context;
174 int64 totalrows = WinGetPartitionRowCount(winobj);
176 Assert(totalrows > 0);
178 up = rank_up(winobj);
179 context = (rank_context *)
180 WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
181 if (up || context->rank == 1)
184 * The current row is not peer to prior row or is just the first, so
185 * count up the number of rows that are peer to the current.
189 context->rank = WinGetCurrentPosition(winobj) + 1;
192 * start from current + 1
194 for (row = context->rank; row < totalrows; row++)
196 if (!WinRowsArePeers(winobj, row - 1, row))
202 PG_RETURN_FLOAT8((float8) context->rank / (float8) totalrows);
207 * compute an exact numeric value with scale 0 (zero),
208 * ranging from 1 (one) to n, per spec.
211 window_ntile(PG_FUNCTION_ARGS)
213 WindowObject winobj = PG_WINDOW_OBJECT();
214 ntile_context *context;
216 context = (ntile_context *)
217 WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));
219 if (context->ntile == 0)
226 total = WinGetPartitionRowCount(winobj);
227 nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull));
230 * per spec: If NT is the null value, then the result is the null
237 * per spec: If NT is less than or equal to 0 (zero), then an
238 * exception condition is raised.
242 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE),
243 errmsg("argument of ntile must be greater than zero")));
246 context->rows_per_bucket = 0;
247 context->boundary = total / nbuckets;
248 if (context->boundary <= 0)
249 context->boundary = 1;
253 * If the total number is not divisible, add 1 row to leading
256 context->remainder = total % nbuckets;
257 if (context->remainder != 0)
262 context->rows_per_bucket++;
263 if (context->boundary < context->rows_per_bucket)
266 if (context->remainder != 0 && context->ntile == context->remainder)
268 context->remainder = 0;
269 context->boundary -= 1;
272 context->rows_per_bucket = 1;
275 PG_RETURN_INT32(context->ntile);
280 * common operation of lead() and lag()
281 * For lead() forward is true, whereas for lag() it is false.
282 * withoffset indicates we have an offset second argument.
283 * withdefault indicates we have a default third argument.
286 leadlag_common(FunctionCallInfo fcinfo,
287 bool forward, bool withoffset, bool withdefault)
289 WindowObject winobj = PG_WINDOW_OBJECT();
298 offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
301 const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
309 result = WinGetFuncArgInPartition(winobj, 0,
310 (forward ? offset : -offset),
318 * target row is out of the partition; supply default value if
319 * provided. otherwise it'll stay NULL
322 result = WinGetFuncArgCurrent(winobj, 2, &isnull);
328 PG_RETURN_DATUM(result);
333 * returns the value of VE evaluated on a row that is 1
334 * row before the current row within a partition,
338 window_lag(PG_FUNCTION_ARGS)
340 return leadlag_common(fcinfo, false, false, false);
345 * returns the value of VE evelulated on a row that is OFFSET
346 * rows before the current row within a partition,
350 window_lag_with_offset(PG_FUNCTION_ARGS)
352 return leadlag_common(fcinfo, false, true, false);
356 * lag_with_offset_and_default
357 * same as lag_with_offset but accepts default value
358 * as its third argument.
361 window_lag_with_offset_and_default(PG_FUNCTION_ARGS)
363 return leadlag_common(fcinfo, false, true, true);
368 * returns the value of VE evaluated on a row that is 1
369 * row after the current row within a partition,
373 window_lead(PG_FUNCTION_ARGS)
375 return leadlag_common(fcinfo, true, false, false);
380 * returns the value of VE evaluated on a row that is OFFSET
381 * number of rows after the current row within a partition,
385 window_lead_with_offset(PG_FUNCTION_ARGS)
387 return leadlag_common(fcinfo, true, true, false);
391 * lead_with_offset_and_default
392 * same as lead_with_offset but accepts default value
393 * as its third argument.
396 window_lead_with_offset_and_default(PG_FUNCTION_ARGS)
398 return leadlag_common(fcinfo, true, true, true);
403 * return the value of VE evaluated on the first row of the
404 * window frame, per spec.
407 window_first_value(PG_FUNCTION_ARGS)
409 WindowObject winobj = PG_WINDOW_OBJECT();
413 result = WinGetFuncArgInFrame(winobj, 0,
414 0, WINDOW_SEEK_HEAD, true,
419 PG_RETURN_DATUM(result);
424 * return the value of VE evaluated on the last row of the
425 * window frame, per spec.
428 window_last_value(PG_FUNCTION_ARGS)
430 WindowObject winobj = PG_WINDOW_OBJECT();
434 result = WinGetFuncArgInFrame(winobj, 0,
435 0, WINDOW_SEEK_TAIL, true,
440 PG_RETURN_DATUM(result);
445 * return the value of VE evaluated on the n-th row from the first
446 * row of the window frame, per spec.
449 window_nth_value(PG_FUNCTION_ARGS)
451 WindowObject winobj = PG_WINDOW_OBJECT();
457 nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
460 const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
464 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE),
465 errmsg("argument of nth_value must be greater than zero")));
467 result = WinGetFuncArgInFrame(winobj, 0,
468 nth - 1, WINDOW_SEEK_HEAD, const_offset,
473 PG_RETURN_DATUM(result);