LCOV - code coverage report
Current view: top level - src/backend/utils/adt - windowfuncs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.6 % 205 196
Test Date: 2026-03-12 06:14:44 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * windowfuncs.c
       4              :  *    Standard window functions defined in SQL spec.
       5              :  *
       6              :  * Portions Copyright (c) 2000-2026, 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
      49        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              :         Assert(curpos == 0);
      62          202 :         context->rank = 1;
      63              :     }
      64              :     else
      65              :     {
      66              :         Assert(curpos > 0);
      67              :         /* do current and prior tuples match by ORDER BY clause? */
      68        82737 :         if (!WinRowsArePeers(winobj, curpos - 1, curpos))
      69        64323 :             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              : {
      86       228901 :     WindowObject winobj = PG_WINDOW_OBJECT();
      87       228901 :     int64       curpos = WinGetCurrentPosition(winobj);
      88              : 
      89       228901 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
      90       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
      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              : 
     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              : 
     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
     139        82759 : window_rank(PG_FUNCTION_ARGS)
     140              : {
     141        82759 :     WindowObject winobj = PG_WINDOW_OBJECT();
     142              :     rank_context *context;
     143              :     bool        up;
     144              : 
     145        82759 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     146        82753 :     up = rank_up(winobj);
     147              :     context = (rank_context *)
     148        82753 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     149        82753 :     if (up)
     150        64284 :         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
     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              : 
     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              : 
     194          101 :     PG_RETURN_POINTER(NULL);
     195              : }
     196              : 
     197              : /*
     198              :  * dense_rank
     199              :  * Rank increases by 1 when key columns change.
     200              :  */
     201              : Datum
     202           72 : window_dense_rank(PG_FUNCTION_ARGS)
     203              : {
     204           72 :     WindowObject winobj = PG_WINDOW_OBJECT();
     205              :     rank_context *context;
     206              :     bool        up;
     207              : 
     208           72 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     209           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
     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              : 
     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              : 
     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
     264           66 : window_percent_rank(PG_FUNCTION_ARGS)
     265              : {
     266           66 :     WindowObject winobj = PG_WINDOW_OBJECT();
     267              :     rank_context *context;
     268              :     bool        up;
     269           66 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     270              : 
     271              :     Assert(totalrows > 0);
     272           66 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     273              : 
     274           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
     292           30 : window_percent_rank_support(PG_FUNCTION_ARGS)
     293              : {
     294           30 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     295              : 
     296           30 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     297              :     {
     298            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              : 
     305           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
     334           66 : window_cume_dist(PG_FUNCTION_ARGS)
     335              : {
     336           66 :     WindowObject winobj = PG_WINDOW_OBJECT();
     337              :     rank_context *context;
     338              :     bool        up;
     339           66 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     340              : 
     341              :     Assert(totalrows > 0);
     342           66 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     343              : 
     344           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
     376           30 : window_cume_dist_support(PG_FUNCTION_ARGS)
     377              : {
     378           30 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     379              : 
     380           30 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     381              :     {
     382            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              : 
     389           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
     416           93 : window_ntile(PG_FUNCTION_ARGS)
     417              : {
     418           93 :     WindowObject winobj = PG_WINDOW_OBJECT();
     419              :     ntile_context *context;
     420              : 
     421           93 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     422              :     context = (ntile_context *)
     423           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)
     455            0 :             context->boundary = 1;
     456              :         else
     457              :         {
     458              :             /*
     459              :              * If the total number is not divisible, add 1 row to leading
     460              :              * buckets.
     461              :              */
     462           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
     489           72 : window_ntile_support(PG_FUNCTION_ARGS)
     490              : {
     491           72 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     492              : 
     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              : 
     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
     534       118530 : leadlag_common(FunctionCallInfo fcinfo,
     535              :                bool forward, bool withoffset, bool withdefault)
     536              : {
     537       118530 :     WindowObject winobj = PG_WINDOW_OBJECT();
     538              :     int32       offset;
     539              :     bool        const_offset;
     540              :     Datum       result;
     541              :     bool        isnull;
     542              :     bool        isout;
     543              : 
     544       118530 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     545       118530 :     if (withoffset)
     546              :     {
     547          450 :         offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     548          450 :         if (isnull)
     549            0 :             PG_RETURN_NULL();
     550          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              : {
     658         2073 :     WindowObject winobj = PG_WINDOW_OBJECT();
     659              :     Datum       result;
     660              :     bool        isnull;
     661              : 
     662         2073 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     663         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              : {
     680         2421 :     WindowObject winobj = PG_WINDOW_OBJECT();
     681              :     Datum       result;
     682              :     bool        isnull;
     683              : 
     684         2421 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     685         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              : {
     702          483 :     WindowObject winobj = PG_WINDOW_OBJECT();
     703              :     bool        const_offset;
     704              :     Datum       result;
     705              :     bool        isnull;
     706              :     int32       nth;
     707              : 
     708          483 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     709          483 :     nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     710          483 :     if (isnull)
     711            0 :         PG_RETURN_NULL();
     712          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              : }
        

Generated by: LCOV version 2.0-1