LCOV - code coverage report
Current view: top level - src/backend/utils/adt - windowfuncs.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 96.1 % 282 271
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 23 23
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 86.0 % 100 86

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

Generated by: LCOV version 2.3.2-1