LCOV - code coverage report
Current view: top level - src/backend/utils/adt - windowfuncs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 196 205 95.6 %
Date: 2025-10-16 18:17:47 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-2025, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/windowfuncs.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "nodes/parsenodes.h"
      17             : #include "nodes/supportnodes.h"
      18             : #include "utils/fmgrprotos.h"
      19             : #include "windowapi.h"
      20             : 
      21             : /*
      22             :  * ranking process information
      23             :  */
      24             : typedef struct rank_context
      25             : {
      26             :     int64       rank;           /* current rank */
      27             : } rank_context;
      28             : 
      29             : /*
      30             :  * ntile process information
      31             :  */
      32             : typedef struct
      33             : {
      34             :     int32       ntile;          /* current result */
      35             :     int64       rows_per_bucket;    /* row number of current bucket */
      36             :     int64       boundary;       /* how many rows should be in the bucket */
      37             :     int64       remainder;      /* (total rows) % (bucket num) */
      38             : } ntile_context;
      39             : 
      40             : static bool rank_up(WindowObject winobj);
      41             : static Datum leadlag_common(FunctionCallInfo fcinfo,
      42             :                             bool forward, bool withoffset, bool withdefault);
      43             : 
      44             : 
      45             : /*
      46             :  * utility routine for *_rank functions.
      47             :  */
      48             : static bool
      49      165878 : rank_up(WindowObject winobj)
      50             : {
      51      165878 :     bool        up = false;     /* should rank increase? */
      52      165878 :     int64       curpos = WinGetCurrentPosition(winobj);
      53             :     rank_context *context;
      54             : 
      55             :     context = (rank_context *)
      56      165878 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
      57             : 
      58      165878 :     if (context->rank == 0)
      59             :     {
      60             :         /* first call: rank of first row is always 1 */
      61             :         Assert(curpos == 0);
      62         404 :         context->rank = 1;
      63             :     }
      64             :     else
      65             :     {
      66             :         Assert(curpos > 0);
      67             :         /* do current and prior tuples match by ORDER BY clause? */
      68      165474 :         if (!WinRowsArePeers(winobj, curpos - 1, curpos))
      69      134238 :             up = true;
      70             :     }
      71             : 
      72             :     /* We can advance the mark, but only *after* access to prior row */
      73      165878 :     WinSetMarkPosition(winobj, curpos);
      74             : 
      75      165878 :     return up;
      76             : }
      77             : 
      78             : 
      79             : /*
      80             :  * row_number
      81             :  * just increment up from 1 until current partition finishes.
      82             :  */
      83             : Datum
      84      457802 : window_row_number(PG_FUNCTION_ARGS)
      85             : {
      86      457802 :     WindowObject winobj = PG_WINDOW_OBJECT();
      87      457802 :     int64       curpos = WinGetCurrentPosition(winobj);
      88             : 
      89      457802 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
      90      457790 :     WinSetMarkPosition(winobj, curpos);
      91      457790 :     PG_RETURN_INT64(curpos + 1);
      92             : }
      93             : 
      94             : /*
      95             :  * window_row_number_support
      96             :  *      prosupport function for window_row_number()
      97             :  */
      98             : Datum
      99         764 : window_row_number_support(PG_FUNCTION_ARGS)
     100             : {
     101         764 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     102             : 
     103         764 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     104             :     {
     105          60 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     106             : 
     107             :         /* row_number() is monotonically increasing */
     108          60 :         req->monotonic = MONOTONICFUNC_INCREASING;
     109          60 :         PG_RETURN_POINTER(req);
     110             :     }
     111             : 
     112         704 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     113             :     {
     114         334 :         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         334 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     123             :                              FRAMEOPTION_ROWS |
     124             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     125             :                              FRAMEOPTION_END_CURRENT_ROW);
     126             : 
     127         334 :         PG_RETURN_POINTER(req);
     128             :     }
     129             : 
     130         370 :     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      165518 : window_rank(PG_FUNCTION_ARGS)
     140             : {
     141      165518 :     WindowObject winobj = PG_WINDOW_OBJECT();
     142             :     rank_context *context;
     143             :     bool        up;
     144             : 
     145      165518 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     146      165506 :     up = rank_up(winobj);
     147             :     context = (rank_context *)
     148      165506 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     149      165506 :     if (up)
     150      134160 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     151             : 
     152      165506 :     PG_RETURN_INT64(context->rank);
     153             : }
     154             : 
     155             : /*
     156             :  * window_rank_support
     157             :  *      prosupport function for window_rank()
     158             :  */
     159             : Datum
     160         378 : window_rank_support(PG_FUNCTION_ARGS)
     161             : {
     162         378 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     163             : 
     164         378 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     165             :     {
     166          12 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     167             : 
     168             :         /* rank() is monotonically increasing */
     169          12 :         req->monotonic = MONOTONICFUNC_INCREASING;
     170          12 :         PG_RETURN_POINTER(req);
     171             :     }
     172             : 
     173         366 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     174             :     {
     175         164 :         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         164 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     187             :                              FRAMEOPTION_ROWS |
     188             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     189             :                              FRAMEOPTION_END_CURRENT_ROW);
     190             : 
     191         164 :         PG_RETURN_POINTER(req);
     192             :     }
     193             : 
     194         202 :     PG_RETURN_POINTER(NULL);
     195             : }
     196             : 
     197             : /*
     198             :  * dense_rank
     199             :  * Rank increases by 1 when key columns change.
     200             :  */
     201             : Datum
     202         144 : window_dense_rank(PG_FUNCTION_ARGS)
     203             : {
     204         144 :     WindowObject winobj = PG_WINDOW_OBJECT();
     205             :     rank_context *context;
     206             :     bool        up;
     207             : 
     208         144 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     209         132 :     up = rank_up(winobj);
     210             :     context = (rank_context *)
     211         132 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     212         132 :     if (up)
     213          30 :         context->rank++;
     214             : 
     215         132 :     PG_RETURN_INT64(context->rank);
     216             : }
     217             : 
     218             : /*
     219             :  * window_dense_rank_support
     220             :  *      prosupport function for window_dense_rank()
     221             :  */
     222             : Datum
     223         108 : window_dense_rank_support(PG_FUNCTION_ARGS)
     224             : {
     225         108 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     226             : 
     227         108 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     228             :     {
     229          18 :         SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
     230             : 
     231             :         /* dense_rank() is monotonically increasing */
     232          18 :         req->monotonic = MONOTONICFUNC_INCREASING;
     233          18 :         PG_RETURN_POINTER(req);
     234             :     }
     235             : 
     236          90 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     237             :     {
     238          42 :         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          42 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     247             :                              FRAMEOPTION_ROWS |
     248             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     249             :                              FRAMEOPTION_END_CURRENT_ROW);
     250             : 
     251          42 :         PG_RETURN_POINTER(req);
     252             :     }
     253             : 
     254          48 :     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         132 : window_percent_rank(PG_FUNCTION_ARGS)
     265             : {
     266         132 :     WindowObject winobj = PG_WINDOW_OBJECT();
     267             :     rank_context *context;
     268             :     bool        up;
     269         132 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     270             : 
     271             :     Assert(totalrows > 0);
     272         132 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     273             : 
     274         120 :     up = rank_up(winobj);
     275             :     context = (rank_context *)
     276         120 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     277         120 :     if (up)
     278          24 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     279             : 
     280             :     /* return zero if there's only one row, per spec */
     281         120 :     if (totalrows <= 1)
     282           6 :         PG_RETURN_FLOAT8(0.0);
     283             : 
     284         114 :     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          60 : window_percent_rank_support(PG_FUNCTION_ARGS)
     293             : {
     294          60 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     295             : 
     296          60 :     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          60 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     306             :     {
     307          30 :         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          30 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     316             :                              FRAMEOPTION_ROWS |
     317             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     318             :                              FRAMEOPTION_END_CURRENT_ROW);
     319             : 
     320          30 :         PG_RETURN_POINTER(req);
     321             :     }
     322             : 
     323          30 :     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         132 : window_cume_dist(PG_FUNCTION_ARGS)
     335             : {
     336         132 :     WindowObject winobj = PG_WINDOW_OBJECT();
     337             :     rank_context *context;
     338             :     bool        up;
     339         132 :     int64       totalrows = WinGetPartitionRowCount(winobj);
     340             : 
     341             :     Assert(totalrows > 0);
     342         132 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     343             : 
     344         120 :     up = rank_up(winobj);
     345             :     context = (rank_context *)
     346         120 :         WinGetPartitionLocalMemory(winobj, sizeof(rank_context));
     347         120 :     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          54 :         context->rank = WinGetCurrentPosition(winobj) + 1;
     356             : 
     357             :         /*
     358             :          * start from current + 1
     359             :          */
     360         120 :         for (row = context->rank; row < totalrows; row++)
     361             :         {
     362          90 :             if (!WinRowsArePeers(winobj, row - 1, row))
     363          24 :                 break;
     364          66 :             context->rank++;
     365             :         }
     366             :     }
     367             : 
     368         120 :     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          60 : window_cume_dist_support(PG_FUNCTION_ARGS)
     377             : {
     378          60 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     379             : 
     380          60 :     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          60 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     390             :     {
     391          30 :         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          30 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     400             :                              FRAMEOPTION_ROWS |
     401             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     402             :                              FRAMEOPTION_END_CURRENT_ROW);
     403             : 
     404          30 :         PG_RETURN_POINTER(req);
     405             :     }
     406             : 
     407          30 :     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         186 : window_ntile(PG_FUNCTION_ARGS)
     417             : {
     418         186 :     WindowObject winobj = PG_WINDOW_OBJECT();
     419             :     ntile_context *context;
     420             : 
     421         186 :     WinCheckAndInitializeNullTreatment(winobj, false, fcinfo);
     422             :     context = (ntile_context *)
     423         174 :         WinGetPartitionLocalMemory(winobj, sizeof(ntile_context));
     424             : 
     425         174 :     if (context->ntile == 0)
     426             :     {
     427             :         /* first call */
     428             :         int64       total;
     429             :         int32       nbuckets;
     430             :         bool        isnull;
     431             : 
     432          48 :         total = WinGetPartitionRowCount(winobj);
     433          48 :         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          48 :         if (isnull)
     440          12 :             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          36 :         if (nbuckets <= 0)
     447           6 :             ereport(ERROR,
     448             :                     (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE),
     449             :                      errmsg("argument of ntile must be greater than zero")));
     450             : 
     451          30 :         context->ntile = 1;
     452          30 :         context->rows_per_bucket = 0;
     453          30 :         context->boundary = total / nbuckets;
     454          30 :         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          30 :             context->remainder = total % nbuckets;
     463          30 :             if (context->remainder != 0)
     464          18 :                 context->boundary++;
     465             :         }
     466             :     }
     467             : 
     468         156 :     context->rows_per_bucket++;
     469         156 :     if (context->boundary < context->rows_per_bucket)
     470             :     {
     471             :         /* ntile up */
     472          18 :         if (context->remainder != 0 && context->ntile == context->remainder)
     473             :         {
     474           6 :             context->remainder = 0;
     475           6 :             context->boundary -= 1;
     476             :         }
     477          18 :         context->ntile += 1;
     478          18 :         context->rows_per_bucket = 1;
     479             :     }
     480             : 
     481         156 :     PG_RETURN_INT32(context->ntile);
     482             : }
     483             : 
     484             : /*
     485             :  * window_ntile_support
     486             :  *      prosupport function for window_ntile()
     487             :  */
     488             : Datum
     489         144 : window_ntile_support(PG_FUNCTION_ARGS)
     490             : {
     491         144 :     Node       *rawreq = (Node *) PG_GETARG_POINTER(0);
     492             : 
     493         144 :     if (IsA(rawreq, SupportRequestWFuncMonotonic))
     494             :     {
     495          24 :         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          24 :         req->monotonic = MONOTONICFUNC_INCREASING;
     502          24 :         PG_RETURN_POINTER(req);
     503             :     }
     504             : 
     505         120 :     if (IsA(rawreq, SupportRequestOptimizeWindowClause))
     506             :     {
     507          54 :         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          54 :         req->frameOptions = (FRAMEOPTION_NONDEFAULT |
     516             :                              FRAMEOPTION_ROWS |
     517             :                              FRAMEOPTION_START_UNBOUNDED_PRECEDING |
     518             :                              FRAMEOPTION_END_CURRENT_ROW);
     519             : 
     520          54 :         PG_RETURN_POINTER(req);
     521             :     }
     522             : 
     523          66 :     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      237060 : leadlag_common(FunctionCallInfo fcinfo,
     535             :                bool forward, bool withoffset, bool withdefault)
     536             : {
     537      237060 :     WindowObject winobj = PG_WINDOW_OBJECT();
     538             :     int32       offset;
     539             :     bool        const_offset;
     540             :     Datum       result;
     541             :     bool        isnull;
     542             :     bool        isout;
     543             : 
     544      237060 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     545      237060 :     if (withoffset)
     546             :     {
     547         900 :         offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     548         900 :         if (isnull)
     549           0 :             PG_RETURN_NULL();
     550         900 :         const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
     551             :     }
     552             :     else
     553             :     {
     554      236160 :         offset = 1;
     555      236160 :         const_offset = true;
     556             :     }
     557             : 
     558      237060 :     result = WinGetFuncArgInPartition(winobj, 0,
     559             :                                       (forward ? offset : -offset),
     560             :                                       WINDOW_SEEK_CURRENT,
     561             :                                       const_offset,
     562             :                                       &isnull, &isout);
     563             : 
     564      237060 :     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         522 :         if (withdefault)
     571          96 :             result = WinGetFuncArgCurrent(winobj, 2, &isnull);
     572             :     }
     573             : 
     574      237060 :     if (isnull)
     575         498 :         PG_RETURN_NULL();
     576             : 
     577      236562 :     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      235632 : window_lag(PG_FUNCTION_ARGS)
     588             : {
     589      235632 :     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         300 : window_lag_with_offset(PG_FUNCTION_ARGS)
     600             : {
     601         300 :     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         120 : window_lag_with_offset_and_default(PG_FUNCTION_ARGS)
     611             : {
     612         120 :     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         528 : window_lead(PG_FUNCTION_ARGS)
     623             : {
     624         528 :     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         360 : window_lead_with_offset(PG_FUNCTION_ARGS)
     635             : {
     636         360 :     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         120 : window_lead_with_offset_and_default(PG_FUNCTION_ARGS)
     646             : {
     647         120 :     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        4146 : window_first_value(PG_FUNCTION_ARGS)
     657             : {
     658        4146 :     WindowObject winobj = PG_WINDOW_OBJECT();
     659             :     Datum       result;
     660             :     bool        isnull;
     661             : 
     662        4146 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     663        4146 :     result = WinGetFuncArgInFrame(winobj, 0,
     664             :                                   0, WINDOW_SEEK_HEAD, true,
     665             :                                   &isnull, NULL);
     666        4074 :     if (isnull)
     667         456 :         PG_RETURN_NULL();
     668             : 
     669        3618 :     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        4842 : window_last_value(PG_FUNCTION_ARGS)
     679             : {
     680        4842 :     WindowObject winobj = PG_WINDOW_OBJECT();
     681             :     Datum       result;
     682             :     bool        isnull;
     683             : 
     684        4842 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     685        4842 :     result = WinGetFuncArgInFrame(winobj, 0,
     686             :                                   0, WINDOW_SEEK_TAIL, true,
     687             :                                   &isnull, NULL);
     688        4836 :     if (isnull)
     689         450 :         PG_RETURN_NULL();
     690             : 
     691        4386 :     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         966 : window_nth_value(PG_FUNCTION_ARGS)
     701             : {
     702         966 :     WindowObject winobj = PG_WINDOW_OBJECT();
     703             :     bool        const_offset;
     704             :     Datum       result;
     705             :     bool        isnull;
     706             :     int32       nth;
     707             : 
     708         966 :     WinCheckAndInitializeNullTreatment(winobj, true, fcinfo);
     709         966 :     nth = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull));
     710         966 :     if (isnull)
     711           0 :         PG_RETURN_NULL();
     712         966 :     const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1);
     713             : 
     714         966 :     if (nth <= 0)
     715           6 :         ereport(ERROR,
     716             :                 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTH_VALUE),
     717             :                  errmsg("argument of nth_value must be greater than zero")));
     718             : 
     719         960 :     result = WinGetFuncArgInFrame(winobj, 0,
     720             :                                   nth - 1, WINDOW_SEEK_HEAD, const_offset,
     721             :                                   &isnull, NULL);
     722         960 :     if (isnull)
     723         138 :         PG_RETURN_NULL();
     724             : 
     725         822 :     PG_RETURN_DATUM(result);
     726             : }

Generated by: LCOV version 1.16