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

Generated by: LCOV version 1.14