LCOV - code coverage report
Current view: top level - src/backend/utils/adt - windowfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 186 195 95.4 %
Date: 2023-06-06 10:12:12 Functions: 23 23 100.0 %
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-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
      48      165678 : rank_up(WindowObject winobj)
      49             : {
      50      165678 :     bool        up = false;     /* should rank increase? */
      51      165678 :     int64       curpos = WinGetCurrentPosition(winobj);
      52             :     rank_context *context;
      53             : 
      54             :     context = (rank_context *)
      55      165678 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
      56             : 
      57      165678 :     if (context->rank == 0)
      58             :     {
      59             :         /* first call: rank of first row is always 1 */
      60             :         Assert(curpos == 0);
      61         408 :         context->rank = 1;
      62             :     }
      63             :     else
      64             :     {
      65             :         Assert(curpos > 0);
      66             :         /* do current and prior tuples match by ORDER BY clause? */
      67      165270 :         if (!WinRowsArePeers(winobj, curpos - 1, curpos))
      68      134250 :             up = true;
      69             :     }
      70             : 
      71             :     /* We can advance the mark, but only *after* access to prior row */
      72      165678 :     WinSetMarkPosition(winobj, curpos);
      73             : 
      74      165678 :     return up;
      75             : }
      76             : 
      77             : 
      78             : /*
      79             :  * row_number
      80             :  * just increment up from 1 until current partition finishes.
      81             :  */
      82             : Datum
      83      427656 : window_row_number(PG_FUNCTION_ARGS)
      84             : {
      85      427656 :     WindowObject winobj = PG_WINDOW_OBJECT();
      86      427656 :     int64       curpos = WinGetCurrentPosition(winobj);
      87             : 
      88      427656 :     WinSetMarkPosition(winobj, curpos);
      89      427656 :     PG_RETURN_INT64(curpos + 1);
      90             : }
      91             : 
      92             : /*
      93             :  * window_row_number_support
      94             :  *      prosupport function for window_row_number()
      95             :  */
      96             : Datum
      97         594 : window_row_number_support(PG_FUNCTION_ARGS)
      98             : {
      99         594 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     100             : 
     101         594 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     102             :     {
     103          54 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     104             : 
     105             :         /* row_number() is monotonically increasing */
     106          54 :         req->monotonic = MONOTONICFUNC_INCREASING;
     107          54 :         PG_RETURN_POINTER(req);
     108             :     }
     109             : 
     110         540 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     111             :     {
     112         258 :         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         258 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     121             :                              FRAMEOPTION_ROWS |
     122             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     123             :                              FRAMEOPTION_END_CURRENT_ROW);
     124             : 
     125         258 :         PG_RETURN_POINTER(req);
     126             :     }
     127             : 
     128         282 :     PG_RETURN_POINTER(NULL);
     129             : }
     130             : 
     131             : /*
     132             :  * rank
     133             :  * Rank changes when key columns change.
     134             :  * The new rank number is the current row number.
     135             :  */
     136             : Datum
     137      165486 : window_rank(PG_FUNCTION_ARGS)
     138             : {
     139      165486 :     WindowObject winobj = PG_WINDOW_OBJECT();
     140             :     rank_context *context;
     141             :     bool        up;
     142             : 
     143      165486 :     up = rank_up(winobj);
     144             :     context = (rank_context *)
     145      165486 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     146      165486 :     if (up)
     147      134172 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     148             : 
     149      165486 :     PG_RETURN_INT64(context->rank);
     150             : }
     151             : 
     152             : /*
     153             :  * window_rank_support
     154             :  *      prosupport function for window_rank()
     155             :  */
     156             : Datum
     157         354 : window_rank_support(PG_FUNCTION_ARGS)
     158             : {
     159         354 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     160             : 
     161         354 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     162             :     {
     163          12 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     164             : 
     165             :         /* rank() is monotonically increasing */
     166          12 :         req->monotonic = MONOTONICFUNC_INCREASING;
     167          12 :         PG_RETURN_POINTER(req);
     168             :     }
     169             : 
     170         342 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     171             :     {
     172         150 :         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         150 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     184             :                              FRAMEOPTION_ROWS |
     185             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     186             :                              FRAMEOPTION_END_CURRENT_ROW);
     187             : 
     188         150 :         PG_RETURN_POINTER(req);
     189             :     }
     190             : 
     191         192 :     PG_RETURN_POINTER(NULL);
     192             : }
     193             : 
     194             : /*
     195             :  * dense_rank
     196             :  * Rank increases by 1 when key columns change.
     197             :  */
     198             : Datum
     199          72 : window_dense_rank(PG_FUNCTION_ARGS)
     200             : {
     201          72 :     WindowObject winobj = PG_WINDOW_OBJECT();
     202             :     rank_context *context;
     203             :     bool        up;
     204             : 
     205          72 :     up = rank_up(winobj);
     206             :     context = (rank_context *)
     207          72 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     208          72 :     if (up)
     209          30 :         context->rank++;
     210             : 
     211          72 :     PG_RETURN_INT64(context->rank);
     212             : }
     213             : 
     214             : /*
     215             :  * window_dense_rank_support
     216             :  *      prosupport function for window_dense_rank()
     217             :  */
     218             : Datum
     219          72 : window_dense_rank_support(PG_FUNCTION_ARGS)
     220             : {
     221          72 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     222             : 
     223          72 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     224             :     {
     225          18 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     226             : 
     227             :         /* dense_rank() is monotonically increasing */
     228          18 :         req->monotonic = MONOTONICFUNC_INCREASING;
     229          18 :         PG_RETURN_POINTER(req);
     230             :     }
     231             : 
     232          54 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     233             :     {
     234          24 :         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          24 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     243             :                              FRAMEOPTION_ROWS |
     244             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     245             :                              FRAMEOPTION_END_CURRENT_ROW);
     246             : 
     247          24 :         PG_RETURN_POINTER(req);
     248             :     }
     249             : 
     250          30 :     PG_RETURN_POINTER(NULL);
     251             : }
     252             : 
     253             : /*
     254             :  * percent_rank
     255             :  * return fraction between 0 and 1 inclusive,
     256             :  * 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
     260          60 : window_percent_rank(PG_FUNCTION_ARGS)
     261             : {
     262          60 :     WindowObject winobj = PG_WINDOW_OBJECT();
     263             :     rank_context *context;
     264             :     bool        up;
     265          60 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     266             : 
     267             :     Assert(totalrows > 0);
     268             : 
     269          60 :     up = rank_up(winobj);
     270             :     context = (rank_context *)
     271          60 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     272          60 :     if (up)
     273          24 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     274             : 
     275             :     /* return zero if there's only one row, per spec */
     276          60 :     if (totalrows <= 1)
     277           6 :         PG_RETURN_FLOAT8(0.0);
     278             : 
     279          54 :     PG_RETURN_FLOAT8((float8) (context->rank - 1) / (float8) (totalrows - 1));
     280             : }
     281             : 
     282             : /*
     283             :  * window_percent_rank_support
     284             :  *      prosupport function for window_percent_rank()
     285             :  */
     286             : Datum
     287          24 : window_percent_rank_support(PG_FUNCTION_ARGS)
     288             : {
     289          24 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     290             : 
     291          24 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     292             :     {
     293           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             : 
     300          24 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     301             :     {
     302          12 :         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          12 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     311             :                              FRAMEOPTION_ROWS |
     312             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     313             :                              FRAMEOPTION_END_CURRENT_ROW);
     314             : 
     315          12 :         PG_RETURN_POINTER(req);
     316             :     }
     317             : 
     318          12 :     PG_RETURN_POINTER(NULL);
     319             : }
     320             : 
     321             : 
     322             : /*
     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
     329          60 : window_cume_dist(PG_FUNCTION_ARGS)
     330             : {
     331          60 :     WindowObject winobj = PG_WINDOW_OBJECT();
     332             :     rank_context *context;
     333             :     bool        up;
     334          60 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     335             : 
     336             :     Assert(totalrows > 0);
     337             : 
     338          60 :     up = rank_up(winobj);
     339             :     context = (rank_context *)
     340          60 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     341          60 :     if (up || context->rank == 1)
     342             :     {
     343             :         /*
     344             :          * 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             : 
     349          48 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     350             : 
     351             :         /*
     352             :          * start from current + 1
     353             :          */
     354          60 :         for (row = context->rank; row < totalrows; row++)
     355             :         {
     356          36 :             if (!WinRowsArePeers(winobj, row - 1, row))
     357          24 :                 break;
     358          12 :             context->rank++;
     359             :         }
     360             :     }
     361             : 
     362          60 :     PG_RETURN_FLOAT8((float8) context->rank / (float8) totalrows);
     363             : }
     364             : 
     365             : /*
     366             :  * window_cume_dist_support
     367             :  *      prosupport function for window_cume_dist()
     368             :  */
     369             : Datum
     370          24 : window_cume_dist_support(PG_FUNCTION_ARGS)
     371             : {
     372          24 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     373             : 
     374          24 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     375             :     {
     376           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             : 
     383          24 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     384             :     {
     385          12 :         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          12 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     394             :                              FRAMEOPTION_ROWS |
     395             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     396             :                              FRAMEOPTION_END_CURRENT_ROW);
     397             : 
     398          12 :         PG_RETURN_POINTER(req);
     399             :     }
     400             : 
     401          12 :     PG_RETURN_POINTER(NULL);
     402             : }
     403             : 
     404             : /*
     405             :  * ntile
     406             :  * compute an exact numeric value with scale 0 (zero),
     407             :  * ranging from 1 (one) to n, per spec.
     408             :  */
     409             : Datum
     410         114 : window_ntile(PG_FUNCTION_ARGS)
     411             : {
     412         114 :     WindowObject winobj = PG_WINDOW_OBJECT();
     413             :     ntile_context *context;
     414             : 
     415             :     context = (ntile_context *)
     416         114 :         WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));
     417             : 
     418         114 :     if (context->ntile == 0)
     419             :     {
     420             :         /* first call */
     421             :         int64       total;
     422             :         int32       nbuckets;
     423             :         bool        isnull;
     424             : 
     425          42 :         total = WinGetPartitionRowCount(winobj);
     426          42 :         nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull));
     427             : 
     428             :         /*
     429             :          * per spec: If NT is the null value, then the result is the null
     430             :          * value.
     431             :          */
     432          42 :         if (isnull)
     433          12 :             PG_RETURN_NULL();
     434             : 
     435             :         /*
     436             :          * per spec: If NT is less than or equal to 0 (zero), then an
     437             :          * exception condition is raised.
     438             :          */
     439          30 :         if (nbuckets <= 0)
     440           6 :             ereport(ERROR,
     441             :                     (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE),
     442             :                      errmsg("argument of ntile must be greater than zero")));
     443             : 
     444          24 :         context->ntile = 1;
     445          24 :         context->rows_per_bucket = 0;
     446          24 :         context->boundary = total / nbuckets;
     447          24 :         if (context->boundary <= 0)
     448           0 :             context->boundary = 1;
     449             :         else
     450             :         {
     451             :             /*
     452             :              * If the total number is not divisible, add 1 row to leading
     453             :              * buckets.
     454             :              */
     455          24 :             context->remainder = total % nbuckets;
     456          24 :             if (context->remainder != 0)
     457          18 :                 context->boundary++;
     458             :         }
     459             :     }
     460             : 
     461          96 :     context->rows_per_bucket++;
     462          96 :     if (context->boundary < context->rows_per_bucket)
     463             :     {
     464             :         /* ntile up */
     465          18 :         if (context->remainder != 0 && context->ntile == context->remainder)
     466             :         {
     467           6 :             context->remainder = 0;
     468           6 :             context->boundary -= 1;
     469             :         }
     470          18 :         context->ntile += 1;
     471          18 :         context->rows_per_bucket = 1;
     472             :     }
     473             : 
     474          96 :     PG_RETURN_INT32(context->ntile);
     475             : }
     476             : 
     477             : /*
     478             :  * window_ntile_support
     479             :  *      prosupport function for window_ntile()
     480             :  */
     481             : Datum
     482          72 : window_ntile_support(PG_FUNCTION_ARGS)
     483             : {
     484          72 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     485             : 
     486          72 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     487             :     {
     488          12 :         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          12 :         req->monotonic = MONOTONICFUNC_INCREASING;
     495          12 :         PG_RETURN_POINTER(req);
     496             :     }
     497             : 
     498          60 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     499             :     {
     500          24 :         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          24 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     509             :                              FRAMEOPTION_ROWS |
     510             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     511             :                              FRAMEOPTION_END_CURRENT_ROW);
     512             : 
     513          24 :         PG_RETURN_POINTER(req);
     514             :     }
     515             : 
     516          36 :     PG_RETURN_POINTER(NULL);
     517             : }
     518             : 
     519             : /*
     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
     527      182778 : leadlag_common(FunctionCallInfo fcinfo,
     528             :                bool forward, bool withoffset, bool withdefault)
     529             : {
     530      182778 :     WindowObject winobj = PG_WINDOW_OBJECT();
     531             :     int32       offset;
     532             :     bool        const_offset;
     533             :     Datum       result;
     534             :     bool        isnull;
     535             :     bool        isout;
     536             : 
     537      182778 :     if (withoffset)
     538             :     {
     539         540 :         offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     540         540 :         if (isnull)
     541           0 :             PG_RETURN_NULL();
     542         540 :         const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
     543             :     }
     544             :     else
     545             :     {
     546      182238 :         offset = 1;
     547      182238 :         const_offset = true;
     548             :     }
     549             : 
     550      182778 :     result = WinGetFuncArgInPartition(winobj, 0,
     551             :                                       (forward ? offset : -offset),
     552             :                                       WINDOW_SEEK_CURRENT,
     553             :                                       const_offset,
     554             :                                       &isnull, &isout);
     555             : 
     556      182778 :     if (isout)
     557             :     {
     558             :         /*
     559             :          * target row is out of the partition; supply default value if
     560             :          * provided.  otherwise it'll stay NULL
     561             :          */
     562         318 :         if (withdefault)
     563          96 :             result = WinGetFuncArgCurrent(winobj, 2, &isnull);
     564             :     }
     565             : 
     566      182778 :     if (isnull)
     567         222 :         PG_RETURN_NULL();
     568             : 
     569      182556 :     PG_RETURN_DATUM(result);
     570             : }
     571             : 
     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
     579      181938 : window_lag(PG_FUNCTION_ARGS)
     580             : {
     581      181938 :     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,
     588             :  * per spec.
     589             :  */
     590             : Datum
     591         120 : window_lag_with_offset(PG_FUNCTION_ARGS)
     592             : {
     593         120 :     return leadlag_common(fcinfo, false, true, false);
     594             : }
     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
     602         120 : window_lag_with_offset_and_default(PG_FUNCTION_ARGS)
     603             : {
     604         120 :     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
     610             :  * row after the current row within a partition,
     611             :  * per spec.
     612             :  */
     613             : Datum
     614         300 : window_lead(PG_FUNCTION_ARGS)
     615             : {
     616         300 :     return leadlag_common(fcinfo, true, false, false);
     617             : }
     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
     626         180 : window_lead_with_offset(PG_FUNCTION_ARGS)
     627             : {
     628         180 :     return leadlag_common(fcinfo, true, true, false);
     629             : }
     630             : 
     631             : /*
     632             :  * lead_with_offset_and_default
     633             :  * same as lead_with_offset but accepts default value
     634             :  * as its third argument.
     635             :  */
     636             : Datum
     637         120 : window_lead_with_offset_and_default(PG_FUNCTION_ARGS)
     638             : {
     639         120 :     return leadlag_common(fcinfo, true, true, true);
     640             : }
     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
     648        2466 : window_first_value(PG_FUNCTION_ARGS)
     649             : {
     650        2466 :     WindowObject winobj = PG_WINDOW_OBJECT();
     651             :     Datum       result;
     652             :     bool        isnull;
     653             : 
     654        2466 :     result = WinGetFuncArgInFrame(winobj, 0,
     655             :                                   0, WINDOW_SEEK_HEAD, true,
     656             :                                   &isnull, NULL);
     657        2448 :     if (isnull)
     658          60 :         PG_RETURN_NULL();
     659             : 
     660        2388 :     PG_RETURN_DATUM(result);
     661             : }
     662             : 
     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
     669        3216 : window_last_value(PG_FUNCTION_ARGS)
     670             : {
     671        3216 :     WindowObject winobj = PG_WINDOW_OBJECT();
     672             :     Datum       result;
     673             :     bool        isnull;
     674             : 
     675        3216 :     result = WinGetFuncArgInFrame(winobj, 0,
     676             :                                   0, WINDOW_SEEK_TAIL, true,
     677             :                                   &isnull, NULL);
     678        3216 :     if (isnull)
     679          60 :         PG_RETURN_NULL();
     680             : 
     681        3156 :     PG_RETURN_DATUM(result);
     682             : }
     683             : 
     684             : /*
     685             :  * nth_value
     686             :  * 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
     690         486 : window_nth_value(PG_FUNCTION_ARGS)
     691             : {
     692         486 :     WindowObject winobj = PG_WINDOW_OBJECT();
     693             :     bool        const_offset;
     694             :     Datum       result;
     695             :     bool        isnull;
     696             :     int32       nth;
     697             : 
     698         486 :     nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     699         486 :     if (isnull)
     700           0 :         PG_RETURN_NULL();
     701         486 :     const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
     702             : 
     703         486 :     if (nth <= 0)
     704           6 :         ereport(ERROR,
     705             :                 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE),
     706             :                  errmsg("argument of nth_value must be greater than zero")));
     707             : 
     708         480 :     result = WinGetFuncArgInFrame(winobj, 0,
     709             :                                   nth - 1, WINDOW_SEEK_HEAD, const_offset,
     710             :                                   &isnull, NULL);
     711         480 :     if (isnull)
     712          54 :         PG_RETURN_NULL();
     713             : 
     714         426 :     PG_RETURN_DATUM(result);
     715             : }

Generated by: LCOV version 1.14