LCOV - code coverage report
Current view: top level - src/backend/executor - nodeWindowAgg.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.5 % 1431 1324
Test Date: 2026-03-14 11:15:04 Functions: 100.0 % 38 38
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeWindowAgg.c
       4              :  *    routines to handle WindowAgg nodes.
       5              :  *
       6              :  * A WindowAgg node evaluates "window functions" across suitable partitions
       7              :  * of the input tuple set.  Any one WindowAgg works for just a single window
       8              :  * specification, though it can evaluate multiple window functions sharing
       9              :  * identical window specifications.  The input tuples are required to be
      10              :  * delivered in sorted order, with the PARTITION BY columns (if any) as
      11              :  * major sort keys and the ORDER BY columns (if any) as minor sort keys.
      12              :  * (The planner generates a stack of WindowAggs with intervening Sort nodes
      13              :  * as needed, if a query involves more than one window specification.)
      14              :  *
      15              :  * Since window functions can require access to any or all of the rows in
      16              :  * the current partition, we accumulate rows of the partition into a
      17              :  * tuplestore.  The window functions are called using the WindowObject API
      18              :  * so that they can access those rows as needed.
      19              :  *
      20              :  * We also support using plain aggregate functions as window functions.
      21              :  * For these, the regular Agg-node environment is emulated for each partition.
      22              :  * As required by the SQL spec, the output represents the value of the
      23              :  * aggregate function over all rows in the current row's window frame.
      24              :  *
      25              :  *
      26              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      27              :  * Portions Copyright (c) 1994, Regents of the University of California
      28              :  *
      29              :  * IDENTIFICATION
      30              :  *    src/backend/executor/nodeWindowAgg.c
      31              :  *
      32              :  *-------------------------------------------------------------------------
      33              :  */
      34              : #include "postgres.h"
      35              : 
      36              : #include "access/htup_details.h"
      37              : #include "catalog/objectaccess.h"
      38              : #include "catalog/pg_aggregate.h"
      39              : #include "catalog/pg_proc.h"
      40              : #include "executor/executor.h"
      41              : #include "executor/nodeWindowAgg.h"
      42              : #include "miscadmin.h"
      43              : #include "nodes/nodeFuncs.h"
      44              : #include "optimizer/clauses.h"
      45              : #include "optimizer/optimizer.h"
      46              : #include "parser/parse_agg.h"
      47              : #include "parser/parse_coerce.h"
      48              : #include "utils/acl.h"
      49              : #include "utils/builtins.h"
      50              : #include "utils/datum.h"
      51              : #include "utils/expandeddatum.h"
      52              : #include "utils/lsyscache.h"
      53              : #include "utils/memutils.h"
      54              : #include "utils/regproc.h"
      55              : #include "utils/syscache.h"
      56              : #include "windowapi.h"
      57              : 
      58              : /*
      59              :  * All the window function APIs are called with this object, which is passed
      60              :  * to window functions as fcinfo->context.
      61              :  */
      62              : typedef struct WindowObjectData
      63              : {
      64              :     NodeTag     type;
      65              :     WindowAggState *winstate;   /* parent WindowAggState */
      66              :     List       *argstates;      /* ExprState trees for fn's arguments */
      67              :     void       *localmem;       /* WinGetPartitionLocalMemory's chunk */
      68              :     int         markptr;        /* tuplestore mark pointer for this fn */
      69              :     int         readptr;        /* tuplestore read pointer for this fn */
      70              :     int64       markpos;        /* row that markptr is positioned on */
      71              :     int64       seekpos;        /* row that readptr is positioned on */
      72              :     uint8     **notnull_info;   /* not null info for each func args */
      73              :     int64      *num_notnull_info;   /* track size (number of tuples in
      74              :                                      * partition) of the notnull_info array
      75              :                                      * for each func args */
      76              : 
      77              :     /*
      78              :      * Null treatment options. One of: NO_NULLTREATMENT, PARSER_IGNORE_NULLS,
      79              :      * PARSER_RESPECT_NULLS or IGNORE_NULLS.
      80              :      */
      81              :     int         ignore_nulls;
      82              : } WindowObjectData;
      83              : 
      84              : /*
      85              :  * We have one WindowStatePerFunc struct for each window function and
      86              :  * window aggregate handled by this node.
      87              :  */
      88              : typedef struct WindowStatePerFuncData
      89              : {
      90              :     /* Links to WindowFunc expr and state nodes this working state is for */
      91              :     WindowFuncExprState *wfuncstate;
      92              :     WindowFunc *wfunc;
      93              : 
      94              :     int         numArguments;   /* number of arguments */
      95              : 
      96              :     FmgrInfo    flinfo;         /* fmgr lookup data for window function */
      97              : 
      98              :     Oid         winCollation;   /* collation derived for window function */
      99              : 
     100              :     /*
     101              :      * We need the len and byval info for the result of each function in order
     102              :      * to know how to copy/delete values.
     103              :      */
     104              :     int16       resulttypeLen;
     105              :     bool        resulttypeByVal;
     106              : 
     107              :     bool        plain_agg;      /* is it just a plain aggregate function? */
     108              :     int         aggno;          /* if so, index of its WindowStatePerAggData */
     109              :     uint8       ignore_nulls;   /* ignore nulls */
     110              : 
     111              :     WindowObject winobj;        /* object used in window function API */
     112              : } WindowStatePerFuncData;
     113              : 
     114              : /*
     115              :  * For plain aggregate window functions, we also have one of these.
     116              :  */
     117              : typedef struct WindowStatePerAggData
     118              : {
     119              :     /* Oids of transition functions */
     120              :     Oid         transfn_oid;
     121              :     Oid         invtransfn_oid; /* may be InvalidOid */
     122              :     Oid         finalfn_oid;    /* may be InvalidOid */
     123              : 
     124              :     /*
     125              :      * fmgr lookup data for transition functions --- only valid when
     126              :      * corresponding oid is not InvalidOid.  Note in particular that fn_strict
     127              :      * flags are kept here.
     128              :      */
     129              :     FmgrInfo    transfn;
     130              :     FmgrInfo    invtransfn;
     131              :     FmgrInfo    finalfn;
     132              : 
     133              :     int         numFinalArgs;   /* number of arguments to pass to finalfn */
     134              : 
     135              :     /*
     136              :      * initial value from pg_aggregate entry
     137              :      */
     138              :     Datum       initValue;
     139              :     bool        initValueIsNull;
     140              : 
     141              :     /*
     142              :      * cached value for current frame boundaries
     143              :      */
     144              :     Datum       resultValue;
     145              :     bool        resultValueIsNull;
     146              : 
     147              :     /*
     148              :      * We need the len and byval info for the agg's input, result, and
     149              :      * transition data types in order to know how to copy/delete values.
     150              :      */
     151              :     int16       inputtypeLen,
     152              :                 resulttypeLen,
     153              :                 transtypeLen;
     154              :     bool        inputtypeByVal,
     155              :                 resulttypeByVal,
     156              :                 transtypeByVal;
     157              : 
     158              :     int         wfuncno;        /* index of associated WindowStatePerFuncData */
     159              : 
     160              :     /* Context holding transition value and possibly other subsidiary data */
     161              :     MemoryContext aggcontext;   /* may be private, or winstate->aggcontext */
     162              : 
     163              :     /* Current transition value */
     164              :     Datum       transValue;     /* current transition value */
     165              :     bool        transValueIsNull;
     166              : 
     167              :     int64       transValueCount;    /* number of currently-aggregated rows */
     168              : 
     169              :     /* Data local to eval_windowaggregates() */
     170              :     bool        restart;        /* need to restart this agg in this cycle? */
     171              : } WindowStatePerAggData;
     172              : 
     173              : static void initialize_windowaggregate(WindowAggState *winstate,
     174              :                                        WindowStatePerFunc perfuncstate,
     175              :                                        WindowStatePerAgg peraggstate);
     176              : static void advance_windowaggregate(WindowAggState *winstate,
     177              :                                     WindowStatePerFunc perfuncstate,
     178              :                                     WindowStatePerAgg peraggstate);
     179              : static bool advance_windowaggregate_base(WindowAggState *winstate,
     180              :                                          WindowStatePerFunc perfuncstate,
     181              :                                          WindowStatePerAgg peraggstate);
     182              : static void finalize_windowaggregate(WindowAggState *winstate,
     183              :                                      WindowStatePerFunc perfuncstate,
     184              :                                      WindowStatePerAgg peraggstate,
     185              :                                      Datum *result, bool *isnull);
     186              : 
     187              : static void eval_windowaggregates(WindowAggState *winstate);
     188              : static void eval_windowfunction(WindowAggState *winstate,
     189              :                                 WindowStatePerFunc perfuncstate,
     190              :                                 Datum *result, bool *isnull);
     191              : 
     192              : static void begin_partition(WindowAggState *winstate);
     193              : static void spool_tuples(WindowAggState *winstate, int64 pos);
     194              : static void release_partition(WindowAggState *winstate);
     195              : 
     196              : static int  row_is_in_frame(WindowObject winobj, int64 pos,
     197              :                             TupleTableSlot *slot, bool fetch_tuple);
     198              : static void update_frameheadpos(WindowAggState *winstate);
     199              : static void update_frametailpos(WindowAggState *winstate);
     200              : static void update_grouptailpos(WindowAggState *winstate);
     201              : 
     202              : static WindowStatePerAggData *initialize_peragg(WindowAggState *winstate,
     203              :                                                 WindowFunc *wfunc,
     204              :                                                 WindowStatePerAgg peraggstate);
     205              : static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
     206              : 
     207              : static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
     208              :                       TupleTableSlot *slot2);
     209              : static bool window_gettupleslot(WindowObject winobj, int64 pos,
     210              :                                 TupleTableSlot *slot);
     211              : 
     212              : static Datum ignorenulls_getfuncarginframe(WindowObject winobj, int argno,
     213              :                                            int relpos, int seektype,
     214              :                                            bool set_mark, bool *isnull,
     215              :                                            bool *isout);
     216              : static Datum gettuple_eval_partition(WindowObject winobj, int argno,
     217              :                                      int64 abs_pos, bool *isnull,
     218              :                                      bool *isout);
     219              : static void init_notnull_info(WindowObject winobj,
     220              :                               WindowStatePerFunc perfuncstate);
     221              : static void grow_notnull_info(WindowObject winobj,
     222              :                               int64 pos, int argno);
     223              : static uint8 get_notnull_info(WindowObject winobj,
     224              :                               int64 pos, int argno);
     225              : static void put_notnull_info(WindowObject winobj,
     226              :                              int64 pos, int argno, bool isnull);
     227              : 
     228              : /*
     229              :  * Not null info bit array consists of 2-bit items
     230              :  */
     231              : #define NN_UNKNOWN  0x00        /* value not calculated yet */
     232              : #define NN_NULL     0x01        /* NULL */
     233              : #define NN_NOTNULL  0x02        /* NOT NULL */
     234              : #define NN_MASK     0x03        /* mask for NOT NULL MAP */
     235              : #define NN_BITS_PER_MEMBER  2   /* number of bits in not null map */
     236              : /* number of items per variable */
     237              : #define NN_ITEM_PER_VAR (BITS_PER_BYTE / NN_BITS_PER_MEMBER)
     238              : /* convert map position to byte offset */
     239              : #define NN_POS_TO_BYTES(pos)    ((pos) / NN_ITEM_PER_VAR)
     240              : /* bytes offset to map position */
     241              : #define NN_BYTES_TO_POS(bytes)  ((bytes) * NN_ITEM_PER_VAR)
     242              : /* calculate shift bits */
     243              : #define NN_SHIFT(pos)   ((pos) % NN_ITEM_PER_VAR) * NN_BITS_PER_MEMBER
     244              : 
     245              : /*
     246              :  * initialize_windowaggregate
     247              :  * parallel to initialize_aggregates in nodeAgg.c
     248              :  */
     249              : static void
     250         2028 : initialize_windowaggregate(WindowAggState *winstate,
     251              :                            WindowStatePerFunc perfuncstate,
     252              :                            WindowStatePerAgg peraggstate)
     253              : {
     254              :     MemoryContext oldContext;
     255              : 
     256              :     /*
     257              :      * If we're using a private aggcontext, we may reset it here.  But if the
     258              :      * context is shared, we don't know which other aggregates may still need
     259              :      * it, so we must leave it to the caller to reset at an appropriate time.
     260              :      */
     261         2028 :     if (peraggstate->aggcontext != winstate->aggcontext)
     262         1451 :         MemoryContextReset(peraggstate->aggcontext);
     263              : 
     264         2028 :     if (peraggstate->initValueIsNull)
     265          781 :         peraggstate->transValue = peraggstate->initValue;
     266              :     else
     267              :     {
     268         1247 :         oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
     269         2494 :         peraggstate->transValue = datumCopy(peraggstate->initValue,
     270         1247 :                                             peraggstate->transtypeByVal,
     271         1247 :                                             peraggstate->transtypeLen);
     272         1247 :         MemoryContextSwitchTo(oldContext);
     273              :     }
     274         2028 :     peraggstate->transValueIsNull = peraggstate->initValueIsNull;
     275         2028 :     peraggstate->transValueCount = 0;
     276         2028 :     peraggstate->resultValue = (Datum) 0;
     277         2028 :     peraggstate->resultValueIsNull = true;
     278         2028 : }
     279              : 
     280              : /*
     281              :  * advance_windowaggregate
     282              :  * parallel to advance_aggregates in nodeAgg.c
     283              :  */
     284              : static void
     285        88646 : advance_windowaggregate(WindowAggState *winstate,
     286              :                         WindowStatePerFunc perfuncstate,
     287              :                         WindowStatePerAgg peraggstate)
     288              : {
     289        88646 :     LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
     290        88646 :     WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
     291        88646 :     int         numArguments = perfuncstate->numArguments;
     292              :     Datum       newVal;
     293              :     ListCell   *arg;
     294              :     int         i;
     295              :     MemoryContext oldContext;
     296        88646 :     ExprContext *econtext = winstate->tmpcontext;
     297        88646 :     ExprState  *filter = wfuncstate->aggfilter;
     298              : 
     299        88646 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     300              : 
     301              :     /* Skip anything FILTERed out */
     302        88646 :     if (filter)
     303              :     {
     304              :         bool        isnull;
     305          171 :         Datum       res = ExecEvalExpr(filter, econtext, &isnull);
     306              : 
     307          171 :         if (isnull || !DatumGetBool(res))
     308              :         {
     309           81 :             MemoryContextSwitchTo(oldContext);
     310           81 :             return;
     311              :         }
     312              :     }
     313              : 
     314              :     /* We start from 1, since the 0th arg will be the transition value */
     315        88565 :     i = 1;
     316       146965 :     foreach(arg, wfuncstate->args)
     317              :     {
     318        58400 :         ExprState  *argstate = (ExprState *) lfirst(arg);
     319              : 
     320        58400 :         fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
     321              :                                              &fcinfo->args[i].isnull);
     322        58400 :         i++;
     323              :     }
     324              : 
     325        88565 :     if (peraggstate->transfn.fn_strict)
     326              :     {
     327              :         /*
     328              :          * For a strict transfn, nothing happens when there's a NULL input; we
     329              :          * just keep the prior transValue.  Note transValueCount doesn't
     330              :          * change either.
     331              :          */
     332        50792 :         for (i = 1; i <= numArguments; i++)
     333              :         {
     334        10315 :             if (fcinfo->args[i].isnull)
     335              :             {
     336           99 :                 MemoryContextSwitchTo(oldContext);
     337           99 :                 return;
     338              :             }
     339              :         }
     340              : 
     341              :         /*
     342              :          * For strict transition functions with initial value NULL we use the
     343              :          * first non-NULL input as the initial state.  (We already checked
     344              :          * that the agg's input type is binary-compatible with its transtype,
     345              :          * so straight copy here is OK.)
     346              :          *
     347              :          * We must copy the datum into aggcontext if it is pass-by-ref.  We do
     348              :          * not need to pfree the old transValue, since it's NULL.
     349              :          */
     350        40477 :         if (peraggstate->transValueCount == 0 && peraggstate->transValueIsNull)
     351              :         {
     352          223 :             MemoryContextSwitchTo(peraggstate->aggcontext);
     353          446 :             peraggstate->transValue = datumCopy(fcinfo->args[1].value,
     354          223 :                                                 peraggstate->transtypeByVal,
     355          223 :                                                 peraggstate->transtypeLen);
     356          223 :             peraggstate->transValueIsNull = false;
     357          223 :             peraggstate->transValueCount = 1;
     358          223 :             MemoryContextSwitchTo(oldContext);
     359          223 :             return;
     360              :         }
     361              : 
     362        40254 :         if (peraggstate->transValueIsNull)
     363              :         {
     364              :             /*
     365              :              * Don't call a strict function with NULL inputs.  Note it is
     366              :              * possible to get here despite the above tests, if the transfn is
     367              :              * strict *and* returned a NULL on a prior cycle.  If that happens
     368              :              * we will propagate the NULL all the way to the end.  That can
     369              :              * only happen if there's no inverse transition function, though,
     370              :              * since we disallow transitions back to NULL when there is one.
     371              :              */
     372            0 :             MemoryContextSwitchTo(oldContext);
     373              :             Assert(!OidIsValid(peraggstate->invtransfn_oid));
     374            0 :             return;
     375              :         }
     376              :     }
     377              : 
     378              :     /*
     379              :      * OK to call the transition function.  Set winstate->curaggcontext while
     380              :      * calling it, for possible use by AggCheckCallContext.
     381              :      */
     382        88243 :     InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
     383              :                              numArguments + 1,
     384              :                              perfuncstate->winCollation,
     385              :                              (Node *) winstate, NULL);
     386        88243 :     fcinfo->args[0].value = peraggstate->transValue;
     387        88243 :     fcinfo->args[0].isnull = peraggstate->transValueIsNull;
     388        88243 :     winstate->curaggcontext = peraggstate->aggcontext;
     389        88243 :     newVal = FunctionCallInvoke(fcinfo);
     390        88237 :     winstate->curaggcontext = NULL;
     391              : 
     392              :     /*
     393              :      * Moving-aggregate transition functions must not return null, see
     394              :      * advance_windowaggregate_base().
     395              :      */
     396        88237 :     if (fcinfo->isnull && OidIsValid(peraggstate->invtransfn_oid))
     397            0 :         ereport(ERROR,
     398              :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     399              :                  errmsg("moving-aggregate transition function must not return null")));
     400              : 
     401              :     /*
     402              :      * We must track the number of rows included in transValue, since to
     403              :      * remove the last input, advance_windowaggregate_base() mustn't call the
     404              :      * inverse transition function, but simply reset transValue back to its
     405              :      * initial value.
     406              :      */
     407        88237 :     peraggstate->transValueCount++;
     408              : 
     409              :     /*
     410              :      * If pass-by-ref datatype, must copy the new value into aggcontext and
     411              :      * free the prior transValue.  But if transfn returned a pointer to its
     412              :      * first input, we don't need to do anything.  Also, if transfn returned a
     413              :      * pointer to a R/W expanded object that is already a child of the
     414              :      * aggcontext, assume we can adopt that value without copying it.  (See
     415              :      * comments for ExecAggCopyTransValue, which this code duplicates.)
     416              :      */
     417        92416 :     if (!peraggstate->transtypeByVal &&
     418         4179 :         DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
     419              :     {
     420          480 :         if (!fcinfo->isnull)
     421              :         {
     422          480 :             MemoryContextSwitchTo(peraggstate->aggcontext);
     423          480 :             if (DatumIsReadWriteExpandedObject(newVal,
     424              :                                                false,
     425          483 :                                                peraggstate->transtypeLen) &&
     426            3 :                 MemoryContextGetParent(DatumGetEOHP(newVal)->eoh_context) == CurrentMemoryContext)
     427              :                  /* do nothing */ ;
     428              :             else
     429          477 :                 newVal = datumCopy(newVal,
     430          477 :                                    peraggstate->transtypeByVal,
     431          477 :                                    peraggstate->transtypeLen);
     432              :         }
     433          480 :         if (!peraggstate->transValueIsNull)
     434              :         {
     435          450 :             if (DatumIsReadWriteExpandedObject(peraggstate->transValue,
     436              :                                                false,
     437              :                                                peraggstate->transtypeLen))
     438            0 :                 DeleteExpandedObject(peraggstate->transValue);
     439              :             else
     440          450 :                 pfree(DatumGetPointer(peraggstate->transValue));
     441              :         }
     442              :     }
     443              : 
     444        88237 :     MemoryContextSwitchTo(oldContext);
     445        88237 :     peraggstate->transValue = newVal;
     446        88237 :     peraggstate->transValueIsNull = fcinfo->isnull;
     447              : }
     448              : 
     449              : /*
     450              :  * advance_windowaggregate_base
     451              :  * Remove the oldest tuple from an aggregation.
     452              :  *
     453              :  * This is very much like advance_windowaggregate, except that we will call
     454              :  * the inverse transition function (which caller must have checked is
     455              :  * available).
     456              :  *
     457              :  * Returns true if we successfully removed the current row from this
     458              :  * aggregate, false if not (in the latter case, caller is responsible
     459              :  * for cleaning up by restarting the aggregation).
     460              :  */
     461              : static bool
     462         2313 : advance_windowaggregate_base(WindowAggState *winstate,
     463              :                              WindowStatePerFunc perfuncstate,
     464              :                              WindowStatePerAgg peraggstate)
     465              : {
     466         2313 :     LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
     467         2313 :     WindowFuncExprState *wfuncstate = perfuncstate->wfuncstate;
     468         2313 :     int         numArguments = perfuncstate->numArguments;
     469              :     Datum       newVal;
     470              :     ListCell   *arg;
     471              :     int         i;
     472              :     MemoryContext oldContext;
     473         2313 :     ExprContext *econtext = winstate->tmpcontext;
     474         2313 :     ExprState  *filter = wfuncstate->aggfilter;
     475              : 
     476         2313 :     oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     477              : 
     478              :     /* Skip anything FILTERed out */
     479         2313 :     if (filter)
     480              :     {
     481              :         bool        isnull;
     482           51 :         Datum       res = ExecEvalExpr(filter, econtext, &isnull);
     483              : 
     484           51 :         if (isnull || !DatumGetBool(res))
     485              :         {
     486           24 :             MemoryContextSwitchTo(oldContext);
     487           24 :             return true;
     488              :         }
     489              :     }
     490              : 
     491              :     /* We start from 1, since the 0th arg will be the transition value */
     492         2289 :     i = 1;
     493         4569 :     foreach(arg, wfuncstate->args)
     494              :     {
     495         2280 :         ExprState  *argstate = (ExprState *) lfirst(arg);
     496              : 
     497         2280 :         fcinfo->args[i].value = ExecEvalExpr(argstate, econtext,
     498              :                                              &fcinfo->args[i].isnull);
     499         2280 :         i++;
     500              :     }
     501              : 
     502         2289 :     if (peraggstate->invtransfn.fn_strict)
     503              :     {
     504              :         /*
     505              :          * For a strict (inv)transfn, nothing happens when there's a NULL
     506              :          * input; we just keep the prior transValue.  Note transValueCount
     507              :          * doesn't change either.
     508              :          */
     509         2802 :         for (i = 1; i <= numArguments; i++)
     510              :         {
     511         1416 :             if (fcinfo->args[i].isnull)
     512              :             {
     513           39 :                 MemoryContextSwitchTo(oldContext);
     514           39 :                 return true;
     515              :             }
     516              :         }
     517              :     }
     518              : 
     519              :     /* There should still be an added but not yet removed value */
     520              :     Assert(peraggstate->transValueCount > 0);
     521              : 
     522              :     /*
     523              :      * In moving-aggregate mode, the state must never be NULL, except possibly
     524              :      * before any rows have been aggregated (which is surely not the case at
     525              :      * this point).  This restriction allows us to interpret a NULL result
     526              :      * from the inverse function as meaning "sorry, can't do an inverse
     527              :      * transition in this case".  We already checked this in
     528              :      * advance_windowaggregate, but just for safety, check again.
     529              :      */
     530         2250 :     if (peraggstate->transValueIsNull)
     531            0 :         elog(ERROR, "aggregate transition value is NULL before inverse transition");
     532              : 
     533              :     /*
     534              :      * We mustn't use the inverse transition function to remove the last
     535              :      * input.  Doing so would yield a non-NULL state, whereas we should be in
     536              :      * the initial state afterwards which may very well be NULL.  So instead,
     537              :      * we simply re-initialize the aggregate in this case.
     538              :      */
     539         2250 :     if (peraggstate->transValueCount == 1)
     540              :     {
     541           45 :         MemoryContextSwitchTo(oldContext);
     542           45 :         initialize_windowaggregate(winstate,
     543           45 :                                    &winstate->perfunc[peraggstate->wfuncno],
     544              :                                    peraggstate);
     545           45 :         return true;
     546              :     }
     547              : 
     548              :     /*
     549              :      * OK to call the inverse transition function.  Set
     550              :      * winstate->curaggcontext while calling it, for possible use by
     551              :      * AggCheckCallContext.
     552              :      */
     553         2205 :     InitFunctionCallInfoData(*fcinfo, &(peraggstate->invtransfn),
     554              :                              numArguments + 1,
     555              :                              perfuncstate->winCollation,
     556              :                              (Node *) winstate, NULL);
     557         2205 :     fcinfo->args[0].value = peraggstate->transValue;
     558         2205 :     fcinfo->args[0].isnull = peraggstate->transValueIsNull;
     559         2205 :     winstate->curaggcontext = peraggstate->aggcontext;
     560         2205 :     newVal = FunctionCallInvoke(fcinfo);
     561         2205 :     winstate->curaggcontext = NULL;
     562              : 
     563              :     /*
     564              :      * If the function returns NULL, report failure, forcing a restart.
     565              :      */
     566         2205 :     if (fcinfo->isnull)
     567              :     {
     568          116 :         MemoryContextSwitchTo(oldContext);
     569          116 :         return false;
     570              :     }
     571              : 
     572              :     /* Update number of rows included in transValue */
     573         2089 :     peraggstate->transValueCount--;
     574              : 
     575              :     /*
     576              :      * If pass-by-ref datatype, must copy the new value into aggcontext and
     577              :      * free the prior transValue.  But if invtransfn returned a pointer to its
     578              :      * first input, we don't need to do anything.  Also, if invtransfn
     579              :      * returned a pointer to a R/W expanded object that is already a child of
     580              :      * the aggcontext, assume we can adopt that value without copying it. (See
     581              :      * comments for ExecAggCopyTransValue, which this code duplicates.)
     582              :      *
     583              :      * Note: the checks for null values here will never fire, but it seems
     584              :      * best to have this stanza look just like advance_windowaggregate.
     585              :      */
     586         3154 :     if (!peraggstate->transtypeByVal &&
     587         1065 :         DatumGetPointer(newVal) != DatumGetPointer(peraggstate->transValue))
     588              :     {
     589          333 :         if (!fcinfo->isnull)
     590              :         {
     591          333 :             MemoryContextSwitchTo(peraggstate->aggcontext);
     592          333 :             if (DatumIsReadWriteExpandedObject(newVal,
     593              :                                                false,
     594          333 :                                                peraggstate->transtypeLen) &&
     595            0 :                 MemoryContextGetParent(DatumGetEOHP(newVal)->eoh_context) == CurrentMemoryContext)
     596              :                  /* do nothing */ ;
     597              :             else
     598          333 :                 newVal = datumCopy(newVal,
     599          333 :                                    peraggstate->transtypeByVal,
     600          333 :                                    peraggstate->transtypeLen);
     601              :         }
     602          333 :         if (!peraggstate->transValueIsNull)
     603              :         {
     604          333 :             if (DatumIsReadWriteExpandedObject(peraggstate->transValue,
     605              :                                                false,
     606              :                                                peraggstate->transtypeLen))
     607            0 :                 DeleteExpandedObject(peraggstate->transValue);
     608              :             else
     609          333 :                 pfree(DatumGetPointer(peraggstate->transValue));
     610              :         }
     611              :     }
     612              : 
     613         2089 :     MemoryContextSwitchTo(oldContext);
     614         2089 :     peraggstate->transValue = newVal;
     615         2089 :     peraggstate->transValueIsNull = fcinfo->isnull;
     616              : 
     617         2089 :     return true;
     618              : }
     619              : 
     620              : /*
     621              :  * finalize_windowaggregate
     622              :  * parallel to finalize_aggregate in nodeAgg.c
     623              :  */
     624              : static void
     625         5379 : finalize_windowaggregate(WindowAggState *winstate,
     626              :                          WindowStatePerFunc perfuncstate,
     627              :                          WindowStatePerAgg peraggstate,
     628              :                          Datum *result, bool *isnull)
     629              : {
     630              :     MemoryContext oldContext;
     631              : 
     632         5379 :     oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
     633              : 
     634              :     /*
     635              :      * Apply the agg's finalfn if one is provided, else return transValue.
     636              :      */
     637         5379 :     if (OidIsValid(peraggstate->finalfn_oid))
     638              :     {
     639         3002 :         LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
     640         3002 :         int         numFinalArgs = peraggstate->numFinalArgs;
     641              :         bool        anynull;
     642              :         int         i;
     643              : 
     644         3002 :         InitFunctionCallInfoData(fcinfodata.fcinfo, &(peraggstate->finalfn),
     645              :                                  numFinalArgs,
     646              :                                  perfuncstate->winCollation,
     647              :                                  (Node *) winstate, NULL);
     648         3002 :         fcinfo->args[0].value =
     649         3002 :             MakeExpandedObjectReadOnly(peraggstate->transValue,
     650              :                                        peraggstate->transValueIsNull,
     651              :                                        peraggstate->transtypeLen);
     652         3002 :         fcinfo->args[0].isnull = peraggstate->transValueIsNull;
     653         3002 :         anynull = peraggstate->transValueIsNull;
     654              : 
     655              :         /* Fill any remaining argument positions with nulls */
     656         3052 :         for (i = 1; i < numFinalArgs; i++)
     657              :         {
     658           50 :             fcinfo->args[i].value = (Datum) 0;
     659           50 :             fcinfo->args[i].isnull = true;
     660           50 :             anynull = true;
     661              :         }
     662              : 
     663         3002 :         if (fcinfo->flinfo->fn_strict && anynull)
     664              :         {
     665              :             /* don't call a strict function with NULL inputs */
     666            0 :             *result = (Datum) 0;
     667            0 :             *isnull = true;
     668              :         }
     669              :         else
     670              :         {
     671              :             Datum       res;
     672              : 
     673         3002 :             winstate->curaggcontext = peraggstate->aggcontext;
     674         3002 :             res = FunctionCallInvoke(fcinfo);
     675         2996 :             winstate->curaggcontext = NULL;
     676         2996 :             *isnull = fcinfo->isnull;
     677         2996 :             *result = MakeExpandedObjectReadOnly(res,
     678              :                                                  fcinfo->isnull,
     679              :                                                  peraggstate->resulttypeLen);
     680              :         }
     681              :     }
     682              :     else
     683              :     {
     684         2377 :         *result =
     685         2377 :             MakeExpandedObjectReadOnly(peraggstate->transValue,
     686              :                                        peraggstate->transValueIsNull,
     687              :                                        peraggstate->transtypeLen);
     688         2377 :         *isnull = peraggstate->transValueIsNull;
     689              :     }
     690              : 
     691         5373 :     MemoryContextSwitchTo(oldContext);
     692         5373 : }
     693              : 
     694              : /*
     695              :  * eval_windowaggregates
     696              :  * evaluate plain aggregates being used as window functions
     697              :  *
     698              :  * This differs from nodeAgg.c in two ways.  First, if the window's frame
     699              :  * start position moves, we use the inverse transition function (if it exists)
     700              :  * to remove rows from the transition value.  And second, we expect to be
     701              :  * able to call aggregate final functions repeatedly after aggregating more
     702              :  * data onto the same transition value.  This is not a behavior required by
     703              :  * nodeAgg.c.
     704              :  */
     705              : static void
     706        80189 : eval_windowaggregates(WindowAggState *winstate)
     707              : {
     708              :     WindowStatePerAgg peraggstate;
     709              :     int         wfuncno,
     710              :                 numaggs,
     711              :                 numaggs_restart,
     712              :                 i;
     713              :     int64       aggregatedupto_nonrestarted;
     714              :     MemoryContext oldContext;
     715              :     ExprContext *econtext;
     716              :     WindowObject agg_winobj;
     717              :     TupleTableSlot *agg_row_slot;
     718              :     TupleTableSlot *temp_slot;
     719              : 
     720        80189 :     numaggs = winstate->numaggs;
     721        80189 :     if (numaggs == 0)
     722            0 :         return;                 /* nothing to do */
     723              : 
     724              :     /* final output execution is in ps_ExprContext */
     725        80189 :     econtext = winstate->ss.ps.ps_ExprContext;
     726        80189 :     agg_winobj = winstate->agg_winobj;
     727        80189 :     agg_row_slot = winstate->agg_row_slot;
     728        80189 :     temp_slot = winstate->temp_slot_1;
     729              : 
     730              :     /*
     731              :      * If the window's frame start clause is UNBOUNDED_PRECEDING and no
     732              :      * exclusion clause is specified, then the window frame consists of a
     733              :      * contiguous group of rows extending forward from the start of the
     734              :      * partition, and rows only enter the frame, never exit it, as the current
     735              :      * row advances forward.  This makes it possible to use an incremental
     736              :      * strategy for evaluating aggregates: we run the transition function for
     737              :      * each row added to the frame, and run the final function whenever we
     738              :      * need the current aggregate value.  This is considerably more efficient
     739              :      * than the naive approach of re-running the entire aggregate calculation
     740              :      * for each current row.  It does assume that the final function doesn't
     741              :      * damage the running transition value, but we have the same assumption in
     742              :      * nodeAgg.c too (when it rescans an existing hash table).
     743              :      *
     744              :      * If the frame start does sometimes move, we can still optimize as above
     745              :      * whenever successive rows share the same frame head, but if the frame
     746              :      * head moves beyond the previous head we try to remove those rows using
     747              :      * the aggregate's inverse transition function.  This function restores
     748              :      * the aggregate's current state to what it would be if the removed row
     749              :      * had never been aggregated in the first place.  Inverse transition
     750              :      * functions may optionally return NULL, indicating that the function was
     751              :      * unable to remove the tuple from aggregation.  If this happens, or if
     752              :      * the aggregate doesn't have an inverse transition function at all, we
     753              :      * must perform the aggregation all over again for all tuples within the
     754              :      * new frame boundaries.
     755              :      *
     756              :      * If there's any exclusion clause, then we may have to aggregate over a
     757              :      * non-contiguous set of rows, so we punt and recalculate for every row.
     758              :      * (For some frame end choices, it might be that the frame is always
     759              :      * contiguous anyway, but that's an optimization to investigate later.)
     760              :      *
     761              :      * In many common cases, multiple rows share the same frame and hence the
     762              :      * same aggregate value. (In particular, if there's no ORDER BY in a RANGE
     763              :      * window, then all rows are peers and so they all have window frame equal
     764              :      * to the whole partition.)  We optimize such cases by calculating the
     765              :      * aggregate value once when we reach the first row of a peer group, and
     766              :      * then returning the saved value for all subsequent rows.
     767              :      *
     768              :      * 'aggregatedupto' keeps track of the first row that has not yet been
     769              :      * accumulated into the aggregate transition values.  Whenever we start a
     770              :      * new peer group, we accumulate forward to the end of the peer group.
     771              :      */
     772              : 
     773              :     /*
     774              :      * First, update the frame head position.
     775              :      *
     776              :      * The frame head should never move backwards, and the code below wouldn't
     777              :      * cope if it did, so for safety we complain if it does.
     778              :      */
     779        80189 :     update_frameheadpos(winstate);
     780        80186 :     if (winstate->frameheadpos < winstate->aggregatedbase)
     781            0 :         elog(ERROR, "window frame head moved backward");
     782              : 
     783              :     /*
     784              :      * If the frame didn't change compared to the previous row, we can re-use
     785              :      * the result values that were previously saved at the bottom of this
     786              :      * function.  Since we don't know the current frame's end yet, this is not
     787              :      * possible to check for fully.  But if the frame end mode is UNBOUNDED
     788              :      * FOLLOWING or CURRENT ROW, no exclusion clause is specified, and the
     789              :      * current row lies within the previous row's frame, then the two frames'
     790              :      * ends must coincide.  Note that on the first row aggregatedbase ==
     791              :      * aggregatedupto, meaning this test must fail, so we don't need to check
     792              :      * the "there was no previous row" case explicitly here.
     793              :      */
     794        80186 :     if (winstate->aggregatedbase == winstate->frameheadpos &&
     795        78297 :         (winstate->frameOptions & (FRAMEOPTION_END_UNBOUNDED_FOLLOWING |
     796        77337 :                                    FRAMEOPTION_END_CURRENT_ROW)) &&
     797        77337 :         !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
     798        77247 :         winstate->aggregatedbase <= winstate->currentpos &&
     799        77229 :         winstate->aggregatedupto > winstate->currentpos)
     800              :     {
     801       151378 :         for (i = 0; i < numaggs; i++)
     802              :         {
     803        75692 :             peraggstate = &winstate->peragg[i];
     804        75692 :             wfuncno = peraggstate->wfuncno;
     805        75692 :             econtext->ecxt_aggvalues[wfuncno] = peraggstate->resultValue;
     806        75692 :             econtext->ecxt_aggnulls[wfuncno] = peraggstate->resultValueIsNull;
     807              :         }
     808        75686 :         return;
     809              :     }
     810              : 
     811              :     /*----------
     812              :      * Initialize restart flags.
     813              :      *
     814              :      * We restart the aggregation:
     815              :      *   - if we're processing the first row in the partition, or
     816              :      *   - if the frame's head moved and we cannot use an inverse
     817              :      *     transition function, or
     818              :      *   - we have an EXCLUSION clause, or
     819              :      *   - if the new frame doesn't overlap the old one
     820              :      *
     821              :      * Note that we don't strictly need to restart in the last case, but if
     822              :      * we're going to remove all rows from the aggregation anyway, a restart
     823              :      * surely is faster.
     824              :      *----------
     825              :      */
     826         4500 :     numaggs_restart = 0;
     827         9891 :     for (i = 0; i < numaggs; i++)
     828              :     {
     829         5391 :         peraggstate = &winstate->peragg[i];
     830         5391 :         if (winstate->currentpos == 0 ||
     831         4354 :             (winstate->aggregatedbase != winstate->frameheadpos &&
     832         2603 :              !OidIsValid(peraggstate->invtransfn_oid)) ||
     833         4316 :             (winstate->frameOptions & FRAMEOPTION_EXCLUSION) ||
     834         3758 :             winstate->aggregatedupto <= winstate->frameheadpos)
     835              :         {
     836         1867 :             peraggstate->restart = true;
     837         1867 :             numaggs_restart++;
     838              :         }
     839              :         else
     840         3524 :             peraggstate->restart = false;
     841              :     }
     842              : 
     843              :     /*
     844              :      * If we have any possibly-moving aggregates, attempt to advance
     845              :      * aggregatedbase to match the frame's head by removing input rows that
     846              :      * fell off the top of the frame from the aggregations.  This can fail,
     847              :      * i.e. advance_windowaggregate_base() can return false, in which case
     848              :      * we'll restart that aggregate below.
     849              :      */
     850         6084 :     while (numaggs_restart < numaggs &&
     851         4319 :            winstate->aggregatedbase < winstate->frameheadpos)
     852              :     {
     853              :         /*
     854              :          * Fetch the next tuple of those being removed. This should never fail
     855              :          * as we should have been here before.
     856              :          */
     857         1584 :         if (!window_gettupleslot(agg_winobj, winstate->aggregatedbase,
     858              :                                  temp_slot))
     859            0 :             elog(ERROR, "could not re-fetch previously fetched frame row");
     860              : 
     861              :         /* Set tuple context for evaluation of aggregate arguments */
     862         1584 :         winstate->tmpcontext->ecxt_outertuple = temp_slot;
     863              : 
     864              :         /*
     865              :          * Perform the inverse transition for each aggregate function in the
     866              :          * window, unless it has already been marked as needing a restart.
     867              :          */
     868         3903 :         for (i = 0; i < numaggs; i++)
     869              :         {
     870              :             bool        ok;
     871              : 
     872         2319 :             peraggstate = &winstate->peragg[i];
     873         2319 :             if (peraggstate->restart)
     874            6 :                 continue;
     875              : 
     876         2313 :             wfuncno = peraggstate->wfuncno;
     877         2313 :             ok = advance_windowaggregate_base(winstate,
     878         2313 :                                               &winstate->perfunc[wfuncno],
     879              :                                               peraggstate);
     880         2313 :             if (!ok)
     881              :             {
     882              :                 /* Inverse transition function has failed, must restart */
     883          116 :                 peraggstate->restart = true;
     884          116 :                 numaggs_restart++;
     885              :             }
     886              :         }
     887              : 
     888              :         /* Reset per-input-tuple context after each tuple */
     889         1584 :         ResetExprContext(winstate->tmpcontext);
     890              : 
     891              :         /* And advance the aggregated-row state */
     892         1584 :         winstate->aggregatedbase++;
     893         1584 :         ExecClearTuple(temp_slot);
     894              :     }
     895              : 
     896              :     /*
     897              :      * If we successfully advanced the base rows of all the aggregates,
     898              :      * aggregatedbase now equals frameheadpos; but if we failed for any, we
     899              :      * must forcibly update aggregatedbase.
     900              :      */
     901         4500 :     winstate->aggregatedbase = winstate->frameheadpos;
     902              : 
     903              :     /*
     904              :      * If we created a mark pointer for aggregates, keep it pushed up to frame
     905              :      * head, so that tuplestore can discard unnecessary rows.
     906              :      */
     907         4500 :     if (agg_winobj->markptr >= 0)
     908         3112 :         WinSetMarkPosition(agg_winobj, winstate->frameheadpos);
     909              : 
     910              :     /*
     911              :      * Now restart the aggregates that require it.
     912              :      *
     913              :      * We assume that aggregates using the shared context always restart if
     914              :      * *any* aggregate restarts, and we may thus clean up the shared
     915              :      * aggcontext if that is the case.  Private aggcontexts are reset by
     916              :      * initialize_windowaggregate() if their owning aggregate restarts. If we
     917              :      * aren't restarting an aggregate, we need to free any previously saved
     918              :      * result for it, else we'll leak memory.
     919              :      */
     920         4500 :     if (numaggs_restart > 0)
     921         1878 :         MemoryContextReset(winstate->aggcontext);
     922         9891 :     for (i = 0; i < numaggs; i++)
     923              :     {
     924         5391 :         peraggstate = &winstate->peragg[i];
     925              : 
     926              :         /* Aggregates using the shared ctx must restart if *any* agg does */
     927              :         Assert(peraggstate->aggcontext != winstate->aggcontext ||
     928              :                numaggs_restart == 0 ||
     929              :                peraggstate->restart);
     930              : 
     931         5391 :         if (peraggstate->restart)
     932              :         {
     933         1983 :             wfuncno = peraggstate->wfuncno;
     934         1983 :             initialize_windowaggregate(winstate,
     935         1983 :                                        &winstate->perfunc[wfuncno],
     936              :                                        peraggstate);
     937              :         }
     938         3408 :         else if (!peraggstate->resultValueIsNull)
     939              :         {
     940         3291 :             if (!peraggstate->resulttypeByVal)
     941         1130 :                 pfree(DatumGetPointer(peraggstate->resultValue));
     942         3291 :             peraggstate->resultValue = (Datum) 0;
     943         3291 :             peraggstate->resultValueIsNull = true;
     944              :         }
     945              :     }
     946              : 
     947              :     /*
     948              :      * Non-restarted aggregates now contain the rows between aggregatedbase
     949              :      * (i.e., frameheadpos) and aggregatedupto, while restarted aggregates
     950              :      * contain no rows.  If there are any restarted aggregates, we must thus
     951              :      * begin aggregating anew at frameheadpos, otherwise we may simply
     952              :      * continue at aggregatedupto.  We must remember the old value of
     953              :      * aggregatedupto to know how long to skip advancing non-restarted
     954              :      * aggregates.  If we modify aggregatedupto, we must also clear
     955              :      * agg_row_slot, per the loop invariant below.
     956              :      */
     957         4500 :     aggregatedupto_nonrestarted = winstate->aggregatedupto;
     958         4500 :     if (numaggs_restart > 0 &&
     959         1878 :         winstate->aggregatedupto != winstate->frameheadpos)
     960              :     {
     961          706 :         winstate->aggregatedupto = winstate->frameheadpos;
     962          706 :         ExecClearTuple(agg_row_slot);
     963              :     }
     964              : 
     965              :     /*
     966              :      * Advance until we reach a row not in frame (or end of partition).
     967              :      *
     968              :      * Note the loop invariant: agg_row_slot is either empty or holds the row
     969              :      * at position aggregatedupto.  We advance aggregatedupto after processing
     970              :      * a row.
     971              :      */
     972              :     for (;;)
     973        88504 :     {
     974              :         int         ret;
     975              : 
     976              :         /* Fetch next row if we didn't already */
     977        93004 :         if (TupIsNull(agg_row_slot))
     978              :         {
     979        91019 :             if (!window_gettupleslot(agg_winobj, winstate->aggregatedupto,
     980              :                                      agg_row_slot))
     981         2083 :                 break;          /* must be end of partition */
     982              :         }
     983              : 
     984              :         /*
     985              :          * Exit loop if no more rows can be in frame.  Skip aggregation if
     986              :          * current row is not in frame but there might be more in the frame.
     987              :          */
     988        90921 :         ret = row_is_in_frame(agg_winobj, winstate->aggregatedupto,
     989              :                               agg_row_slot, false);
     990        90915 :         if (ret < 0)
     991         2405 :             break;
     992        88510 :         if (ret == 0)
     993          948 :             goto next_tuple;
     994              : 
     995              :         /* Set tuple context for evaluation of aggregate arguments */
     996        87562 :         winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
     997              : 
     998              :         /* Accumulate row into the aggregates */
     999       186791 :         for (i = 0; i < numaggs; i++)
    1000              :         {
    1001        99235 :             peraggstate = &winstate->peragg[i];
    1002              : 
    1003              :             /* Non-restarted aggs skip until aggregatedupto_nonrestarted */
    1004        99235 :             if (!peraggstate->restart &&
    1005        60469 :                 winstate->aggregatedupto < aggregatedupto_nonrestarted)
    1006        10589 :                 continue;
    1007              : 
    1008        88646 :             wfuncno = peraggstate->wfuncno;
    1009        88646 :             advance_windowaggregate(winstate,
    1010        88646 :                                     &winstate->perfunc[wfuncno],
    1011              :                                     peraggstate);
    1012              :         }
    1013              : 
    1014        87556 : next_tuple:
    1015              :         /* Reset per-input-tuple context after each tuple */
    1016        88504 :         ResetExprContext(winstate->tmpcontext);
    1017              : 
    1018              :         /* And advance the aggregated-row state */
    1019        88504 :         winstate->aggregatedupto++;
    1020        88504 :         ExecClearTuple(agg_row_slot);
    1021              :     }
    1022              : 
    1023              :     /* The frame's end is not supposed to move backwards, ever */
    1024              :     Assert(aggregatedupto_nonrestarted <= winstate->aggregatedupto);
    1025              : 
    1026              :     /*
    1027              :      * finalize aggregates and fill result/isnull fields.
    1028              :      */
    1029         9861 :     for (i = 0; i < numaggs; i++)
    1030              :     {
    1031              :         Datum      *result;
    1032              :         bool       *isnull;
    1033              : 
    1034         5379 :         peraggstate = &winstate->peragg[i];
    1035         5379 :         wfuncno = peraggstate->wfuncno;
    1036         5379 :         result = &econtext->ecxt_aggvalues[wfuncno];
    1037         5379 :         isnull = &econtext->ecxt_aggnulls[wfuncno];
    1038         5379 :         finalize_windowaggregate(winstate,
    1039         5379 :                                  &winstate->perfunc[wfuncno],
    1040              :                                  peraggstate,
    1041              :                                  result, isnull);
    1042              : 
    1043              :         /*
    1044              :          * save the result in case next row shares the same frame.
    1045              :          *
    1046              :          * XXX in some framing modes, eg ROWS/END_CURRENT_ROW, we can know in
    1047              :          * advance that the next row can't possibly share the same frame. Is
    1048              :          * it worth detecting that and skipping this code?
    1049              :          */
    1050         5373 :         if (!peraggstate->resulttypeByVal && !*isnull)
    1051              :         {
    1052         1436 :             oldContext = MemoryContextSwitchTo(peraggstate->aggcontext);
    1053         1436 :             peraggstate->resultValue =
    1054         1436 :                 datumCopy(*result,
    1055         1436 :                           peraggstate->resulttypeByVal,
    1056         1436 :                           peraggstate->resulttypeLen);
    1057         1436 :             MemoryContextSwitchTo(oldContext);
    1058              :         }
    1059              :         else
    1060              :         {
    1061         3937 :             peraggstate->resultValue = *result;
    1062              :         }
    1063         5373 :         peraggstate->resultValueIsNull = *isnull;
    1064              :     }
    1065              : }
    1066              : 
    1067              : /*
    1068              :  * eval_windowfunction
    1069              :  *
    1070              :  * Arguments of window functions are not evaluated here, because a window
    1071              :  * function can need random access to arbitrary rows in the partition.
    1072              :  * The window function uses the special WinGetFuncArgInPartition and
    1073              :  * WinGetFuncArgInFrame functions to evaluate the arguments for the rows
    1074              :  * it wants.
    1075              :  */
    1076              : static void
    1077       435464 : eval_windowfunction(WindowAggState *winstate, WindowStatePerFunc perfuncstate,
    1078              :                     Datum *result, bool *isnull)
    1079              : {
    1080       435464 :     LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
    1081              :     MemoryContext oldContext;
    1082              : 
    1083       435464 :     oldContext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
    1084              : 
    1085              :     /*
    1086              :      * We don't pass any normal arguments to a window function, but we do pass
    1087              :      * it the number of arguments, in order to permit window function
    1088              :      * implementations to support varying numbers of arguments.  The real info
    1089              :      * goes through the WindowObject, which is passed via fcinfo->context.
    1090              :      */
    1091       435464 :     InitFunctionCallInfoData(*fcinfo, &(perfuncstate->flinfo),
    1092              :                              perfuncstate->numArguments,
    1093              :                              perfuncstate->winCollation,
    1094              :                              (Node *) perfuncstate->winobj, NULL);
    1095              :     /* Just in case, make all the regular argument slots be null */
    1096       560117 :     for (int argno = 0; argno < perfuncstate->numArguments; argno++)
    1097       124653 :         fcinfo->args[argno].isnull = true;
    1098              :     /* Window functions don't have a current aggregate context, either */
    1099       435464 :     winstate->curaggcontext = NULL;
    1100              : 
    1101       435464 :     *result = FunctionCallInvoke(fcinfo);
    1102       435383 :     *isnull = fcinfo->isnull;
    1103              : 
    1104              :     /*
    1105              :      * The window function might have returned a pass-by-ref result that's
    1106              :      * just a pointer into one of the WindowObject's temporary slots.  That's
    1107              :      * not a problem if it's the only window function using the WindowObject;
    1108              :      * but if there's more than one function, we'd better copy the result to
    1109              :      * ensure it's not clobbered by later window functions.
    1110              :      */
    1111       435383 :     if (!perfuncstate->resulttypeByVal && !fcinfo->isnull &&
    1112          510 :         winstate->numfuncs > 1)
    1113           54 :         *result = datumCopy(*result,
    1114           54 :                             perfuncstate->resulttypeByVal,
    1115           54 :                             perfuncstate->resulttypeLen);
    1116              : 
    1117       435383 :     MemoryContextSwitchTo(oldContext);
    1118       435383 : }
    1119              : 
    1120              : /*
    1121              :  * prepare_tuplestore
    1122              :  *      Prepare the tuplestore and all of the required read pointers for the
    1123              :  *      WindowAggState's frameOptions.
    1124              :  *
    1125              :  * Note: We use pg_noinline to avoid bloating the calling function with code
    1126              :  * which is only called once.
    1127              :  */
    1128              : static pg_noinline void
    1129         1152 : prepare_tuplestore(WindowAggState *winstate)
    1130              : {
    1131         1152 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    1132         1152 :     int         frameOptions = winstate->frameOptions;
    1133         1152 :     int         numfuncs = winstate->numfuncs;
    1134              : 
    1135              :     /* we shouldn't be called if this was done already */
    1136              :     Assert(winstate->buffer == NULL);
    1137              : 
    1138              :     /* Create new tuplestore */
    1139         1152 :     winstate->buffer = tuplestore_begin_heap(false, false, work_mem);
    1140              : 
    1141              :     /*
    1142              :      * Set up read pointers for the tuplestore.  The current pointer doesn't
    1143              :      * need BACKWARD capability, but the per-window-function read pointers do,
    1144              :      * and the aggregate pointer does if we might need to restart aggregation.
    1145              :      */
    1146         1152 :     winstate->current_ptr = 0;   /* read pointer 0 is pre-allocated */
    1147              : 
    1148              :     /* reset default REWIND capability bit for current ptr */
    1149         1152 :     tuplestore_set_eflags(winstate->buffer, 0);
    1150              : 
    1151              :     /* create read pointers for aggregates, if needed */
    1152         1152 :     if (winstate->numaggs > 0)
    1153              :     {
    1154          571 :         WindowObject agg_winobj = winstate->agg_winobj;
    1155          571 :         int         readptr_flags = 0;
    1156              : 
    1157              :         /*
    1158              :          * If the frame head is potentially movable, or we have an EXCLUSION
    1159              :          * clause, we might need to restart aggregation ...
    1160              :          */
    1161          571 :         if (!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
    1162          204 :             (frameOptions & FRAMEOPTION_EXCLUSION))
    1163              :         {
    1164              :             /* ... so create a mark pointer to track the frame head */
    1165          376 :             agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
    1166              :             /* and the read pointer will need BACKWARD capability */
    1167          376 :             readptr_flags |= EXEC_FLAG_BACKWARD;
    1168              :         }
    1169              : 
    1170          571 :         agg_winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
    1171              :                                                             readptr_flags);
    1172              :     }
    1173              : 
    1174              :     /* create mark and read pointers for each real window function */
    1175         2685 :     for (int i = 0; i < numfuncs; i++)
    1176              :     {
    1177         1533 :         WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
    1178              : 
    1179         1533 :         if (!perfuncstate->plain_agg)
    1180              :         {
    1181          914 :             WindowObject winobj = perfuncstate->winobj;
    1182              : 
    1183          914 :             winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer,
    1184              :                                                             0);
    1185          914 :             winobj->readptr = tuplestore_alloc_read_pointer(winstate->buffer,
    1186              :                                                             EXEC_FLAG_BACKWARD);
    1187              :         }
    1188              :     }
    1189              : 
    1190              :     /*
    1191              :      * If we are in RANGE or GROUPS mode, then determining frame boundaries
    1192              :      * requires physical access to the frame endpoint rows, except in certain
    1193              :      * degenerate cases.  We create read pointers to point to those rows, to
    1194              :      * simplify access and ensure that the tuplestore doesn't discard the
    1195              :      * endpoint rows prematurely.  (Must create pointers in exactly the same
    1196              :      * cases that update_frameheadpos and update_frametailpos need them.)
    1197              :      */
    1198         1152 :     winstate->framehead_ptr = winstate->frametail_ptr = -1; /* if not used */
    1199              : 
    1200         1152 :     if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    1201              :     {
    1202          640 :         if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
    1203           34 :              node->ordNumCols != 0) ||
    1204          606 :             (frameOptions & FRAMEOPTION_START_OFFSET))
    1205          361 :             winstate->framehead_ptr =
    1206          361 :                 tuplestore_alloc_read_pointer(winstate->buffer, 0);
    1207          640 :         if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
    1208          255 :              node->ordNumCols != 0) ||
    1209          471 :             (frameOptions & FRAMEOPTION_END_OFFSET))
    1210          526 :             winstate->frametail_ptr =
    1211          526 :                 tuplestore_alloc_read_pointer(winstate->buffer, 0);
    1212              :     }
    1213              : 
    1214              :     /*
    1215              :      * If we have an exclusion clause that requires knowing the boundaries of
    1216              :      * the current row's peer group, we create a read pointer to track the
    1217              :      * tail position of the peer group (i.e., first row of the next peer
    1218              :      * group).  The head position does not require its own pointer because we
    1219              :      * maintain that as a side effect of advancing the current row.
    1220              :      */
    1221         1152 :     winstate->grouptail_ptr = -1;
    1222              : 
    1223         1152 :     if ((frameOptions & (FRAMEOPTION_EXCLUDE_GROUP |
    1224           90 :                          FRAMEOPTION_EXCLUDE_TIES)) &&
    1225           90 :         node->ordNumCols != 0)
    1226              :     {
    1227           84 :         winstate->grouptail_ptr =
    1228           84 :             tuplestore_alloc_read_pointer(winstate->buffer, 0);
    1229              :     }
    1230         1152 : }
    1231              : 
    1232              : /*
    1233              :  * begin_partition
    1234              :  * Start buffering rows of the next partition.
    1235              :  */
    1236              : static void
    1237         1813 : begin_partition(WindowAggState *winstate)
    1238              : {
    1239         1813 :     PlanState  *outerPlan = outerPlanState(winstate);
    1240         1813 :     int         numfuncs = winstate->numfuncs;
    1241              : 
    1242         1813 :     winstate->partition_spooled = false;
    1243         1813 :     winstate->framehead_valid = false;
    1244         1813 :     winstate->frametail_valid = false;
    1245         1813 :     winstate->grouptail_valid = false;
    1246         1813 :     winstate->spooled_rows = 0;
    1247         1813 :     winstate->currentpos = 0;
    1248         1813 :     winstate->frameheadpos = 0;
    1249         1813 :     winstate->frametailpos = 0;
    1250         1813 :     winstate->currentgroup = 0;
    1251         1813 :     winstate->frameheadgroup = 0;
    1252         1813 :     winstate->frametailgroup = 0;
    1253         1813 :     winstate->groupheadpos = 0;
    1254         1813 :     winstate->grouptailpos = -1; /* see update_grouptailpos */
    1255         1813 :     ExecClearTuple(winstate->agg_row_slot);
    1256         1813 :     if (winstate->framehead_slot)
    1257          512 :         ExecClearTuple(winstate->framehead_slot);
    1258         1813 :     if (winstate->frametail_slot)
    1259          863 :         ExecClearTuple(winstate->frametail_slot);
    1260              : 
    1261              :     /*
    1262              :      * If this is the very first partition, we need to fetch the first input
    1263              :      * row to store in first_part_slot.
    1264              :      */
    1265         1813 :     if (TupIsNull(winstate->first_part_slot))
    1266              :     {
    1267         1191 :         TupleTableSlot *outerslot = ExecProcNode(outerPlan);
    1268              : 
    1269         1191 :         if (!TupIsNull(outerslot))
    1270         1182 :             ExecCopySlot(winstate->first_part_slot, outerslot);
    1271              :         else
    1272              :         {
    1273              :             /* outer plan is empty, so we have nothing to do */
    1274            9 :             winstate->partition_spooled = true;
    1275            9 :             winstate->more_partitions = false;
    1276            9 :             return;
    1277              :         }
    1278              :     }
    1279              : 
    1280              :     /* Create new tuplestore if not done already. */
    1281         1804 :     if (unlikely(winstate->buffer == NULL))
    1282         1152 :         prepare_tuplestore(winstate);
    1283              : 
    1284         1804 :     winstate->next_partition = false;
    1285              : 
    1286         1804 :     if (winstate->numaggs > 0)
    1287              :     {
    1288          941 :         WindowObject agg_winobj = winstate->agg_winobj;
    1289              : 
    1290              :         /* reset mark and see positions for aggregate functions */
    1291          941 :         agg_winobj->markpos = -1;
    1292          941 :         agg_winobj->seekpos = -1;
    1293              : 
    1294              :         /* Also reset the row counters for aggregates */
    1295          941 :         winstate->aggregatedbase = 0;
    1296          941 :         winstate->aggregatedupto = 0;
    1297              :     }
    1298              : 
    1299              :     /* reset mark and seek positions for each real window function */
    1300         4142 :     for (int i = 0; i < numfuncs; i++)
    1301              :     {
    1302         2338 :         WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
    1303              : 
    1304         2338 :         if (!perfuncstate->plain_agg)
    1305              :         {
    1306         1298 :             WindowObject winobj = perfuncstate->winobj;
    1307              : 
    1308         1298 :             winobj->markpos = -1;
    1309         1298 :             winobj->seekpos = -1;
    1310              : 
    1311              :             /* reset null map */
    1312         1298 :             if (winobj->ignore_nulls == IGNORE_NULLS ||
    1313         1283 :                 winobj->ignore_nulls == PARSER_IGNORE_NULLS)
    1314              :             {
    1315          108 :                 int         numargs = perfuncstate->numArguments;
    1316              : 
    1317          249 :                 for (int j = 0; j < numargs; j++)
    1318              :                 {
    1319          141 :                     int         n = winobj->num_notnull_info[j];
    1320              : 
    1321          141 :                     if (n > 0)
    1322           15 :                         memset(winobj->notnull_info[j], 0,
    1323           15 :                                NN_POS_TO_BYTES(n));
    1324              :                 }
    1325              :             }
    1326              :         }
    1327              :     }
    1328              : 
    1329              :     /*
    1330              :      * Store the first tuple into the tuplestore (it's always available now;
    1331              :      * we either read it above, or saved it at the end of previous partition)
    1332              :      */
    1333         1804 :     tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
    1334         1804 :     winstate->spooled_rows++;
    1335              : }
    1336              : 
    1337              : /*
    1338              :  * Read tuples from the outer node, up to and including position 'pos', and
    1339              :  * store them into the tuplestore. If pos is -1, reads the whole partition.
    1340              :  */
    1341              : static void
    1342       930483 : spool_tuples(WindowAggState *winstate, int64 pos)
    1343              : {
    1344       930483 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    1345              :     PlanState  *outerPlan;
    1346              :     TupleTableSlot *outerslot;
    1347              :     MemoryContext oldcontext;
    1348              : 
    1349       930483 :     if (!winstate->buffer)
    1350            3 :         return;                 /* just a safety check */
    1351       930480 :     if (winstate->partition_spooled)
    1352        62249 :         return;                 /* whole partition done already */
    1353              : 
    1354              :     /*
    1355              :      * When in pass-through mode we can just exhaust all tuples in the current
    1356              :      * partition.  We don't need these tuples for any further window function
    1357              :      * evaluation, however, we do need to keep them around if we're not the
    1358              :      * top-level window as another WindowAgg node above must see these.
    1359              :      */
    1360       868231 :     if (winstate->status != WINDOWAGG_RUN)
    1361              :     {
    1362              :         Assert(winstate->status == WINDOWAGG_PASSTHROUGH ||
    1363              :                winstate->status == WINDOWAGG_PASSTHROUGH_STRICT);
    1364              : 
    1365           15 :         pos = -1;
    1366              :     }
    1367              : 
    1368              :     /*
    1369              :      * If the tuplestore has spilled to disk, alternate reading and writing
    1370              :      * becomes quite expensive due to frequent buffer flushes.  It's cheaper
    1371              :      * to force the entire partition to get spooled in one go.
    1372              :      *
    1373              :      * XXX this is a horrid kluge --- it'd be better to fix the performance
    1374              :      * problem inside tuplestore.  FIXME
    1375              :      */
    1376       868216 :     else if (!tuplestore_in_memory(winstate->buffer))
    1377            6 :         pos = -1;
    1378              : 
    1379       868231 :     outerPlan = outerPlanState(winstate);
    1380              : 
    1381              :     /* Must be in query context to call outerplan */
    1382       868231 :     oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    1383              : 
    1384      2247464 :     while (winstate->spooled_rows <= pos || pos == -1)
    1385              :     {
    1386       512707 :         outerslot = ExecProcNode(outerPlan);
    1387       512707 :         if (TupIsNull(outerslot))
    1388              :         {
    1389              :             /* reached the end of the last partition */
    1390         1083 :             winstate->partition_spooled = true;
    1391         1083 :             winstate->more_partitions = false;
    1392         1083 :             break;
    1393              :         }
    1394              : 
    1395       511624 :         if (node->partNumCols > 0)
    1396              :         {
    1397        69351 :             ExprContext *econtext = winstate->tmpcontext;
    1398              : 
    1399        69351 :             econtext->ecxt_innertuple = winstate->first_part_slot;
    1400        69351 :             econtext->ecxt_outertuple = outerslot;
    1401              : 
    1402              :             /* Check if this tuple still belongs to the current partition */
    1403        69351 :             if (!ExecQualAndReset(winstate->partEqfunction, econtext))
    1404              :             {
    1405              :                 /*
    1406              :                  * end of partition; copy the tuple for the next cycle.
    1407              :                  */
    1408          622 :                 ExecCopySlot(winstate->first_part_slot, outerslot);
    1409          622 :                 winstate->partition_spooled = true;
    1410          622 :                 winstate->more_partitions = true;
    1411          622 :                 break;
    1412              :             }
    1413              :         }
    1414              : 
    1415              :         /*
    1416              :          * Remember the tuple unless we're the top-level window and we're in
    1417              :          * pass-through mode.
    1418              :          */
    1419       511002 :         if (winstate->status != WINDOWAGG_PASSTHROUGH_STRICT)
    1420              :         {
    1421              :             /* Still in partition, so save it into the tuplestore */
    1422       510996 :             tuplestore_puttupleslot(winstate->buffer, outerslot);
    1423       510996 :             winstate->spooled_rows++;
    1424              :         }
    1425              :     }
    1426              : 
    1427       868231 :     MemoryContextSwitchTo(oldcontext);
    1428              : }
    1429              : 
    1430              : /*
    1431              :  * release_partition
    1432              :  * clear information kept within a partition, including
    1433              :  * tuplestore and aggregate results.
    1434              :  */
    1435              : static void
    1436         3055 : release_partition(WindowAggState *winstate)
    1437              : {
    1438              :     int         i;
    1439              : 
    1440         6983 :     for (i = 0; i < winstate->numfuncs; i++)
    1441              :     {
    1442         3928 :         WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
    1443              : 
    1444              :         /* Release any partition-local state of this window function */
    1445         3928 :         if (perfuncstate->winobj)
    1446         2063 :             perfuncstate->winobj->localmem = NULL;
    1447              :     }
    1448              : 
    1449              :     /*
    1450              :      * Release all partition-local memory (in particular, any partition-local
    1451              :      * state that we might have trashed our pointers to in the above loop, and
    1452              :      * any aggregate temp data).  We don't rely on retail pfree because some
    1453              :      * aggregates might have allocated data we don't have direct pointers to.
    1454              :      */
    1455         3055 :     MemoryContextReset(winstate->partcontext);
    1456         3055 :     MemoryContextReset(winstate->aggcontext);
    1457         4920 :     for (i = 0; i < winstate->numaggs; i++)
    1458              :     {
    1459         1865 :         if (winstate->peragg[i].aggcontext != winstate->aggcontext)
    1460          966 :             MemoryContextReset(winstate->peragg[i].aggcontext);
    1461              :     }
    1462              : 
    1463         3055 :     if (winstate->buffer)
    1464         1720 :         tuplestore_clear(winstate->buffer);
    1465         3055 :     winstate->partition_spooled = false;
    1466         3055 :     winstate->next_partition = true;
    1467         3055 : }
    1468              : 
    1469              : /*
    1470              :  * row_is_in_frame
    1471              :  * Determine whether a row is in the current row's window frame according
    1472              :  * to our window framing rule
    1473              :  *
    1474              :  * The caller must have already determined that the row is in the partition
    1475              :  * and fetched it into a slot if fetch_tuple is false.
    1476              :  * This function just encapsulates the framing rules.
    1477              :  *
    1478              :  * Returns:
    1479              :  * -1, if the row is out of frame and no succeeding rows can be in frame
    1480              :  * 0, if the row is out of frame but succeeding rows might be in frame
    1481              :  * 1, if the row is in frame
    1482              :  *
    1483              :  * May clobber winstate->temp_slot_2.
    1484              :  */
    1485              : static int
    1486        96303 : row_is_in_frame(WindowObject winobj, int64 pos, TupleTableSlot *slot,
    1487              :                 bool fetch_tuple)
    1488              : {
    1489        96303 :     WindowAggState *winstate = winobj->winstate;
    1490        96303 :     int         frameOptions = winstate->frameOptions;
    1491              : 
    1492              :     Assert(pos >= 0);            /* else caller error */
    1493              : 
    1494              :     /*
    1495              :      * First, check frame starting conditions.  We might as well delegate this
    1496              :      * to update_frameheadpos always; it doesn't add any notable cost.
    1497              :      */
    1498        96303 :     update_frameheadpos(winstate);
    1499        96303 :     if (pos < winstate->frameheadpos)
    1500           72 :         return 0;
    1501              : 
    1502              :     /*
    1503              :      * Okay so far, now check frame ending conditions.  Here, we avoid calling
    1504              :      * update_frametailpos in simple cases, so as not to spool tuples further
    1505              :      * ahead than necessary.
    1506              :      */
    1507        96231 :     if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
    1508              :     {
    1509        79182 :         if (frameOptions & FRAMEOPTION_ROWS)
    1510              :         {
    1511              :             /* rows after current row are out of frame */
    1512         1104 :             if (pos > winstate->currentpos)
    1513          486 :                 return -1;
    1514              :         }
    1515        78078 :         else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    1516              :         {
    1517              :             /* following row that is not peer is out of frame */
    1518        78078 :             if (pos > winstate->currentpos)
    1519              :             {
    1520        76330 :                 if (fetch_tuple)    /* need to fetch tuple? */
    1521            0 :                     if (!window_gettupleslot(winobj, pos, slot))
    1522            0 :                         return -1;
    1523        76330 :                 if (!are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
    1524          686 :                     return -1;
    1525              :             }
    1526              :         }
    1527              :         else
    1528              :             Assert(false);
    1529              :     }
    1530        17049 :     else if (frameOptions & FRAMEOPTION_END_OFFSET)
    1531              :     {
    1532         9957 :         if (frameOptions & FRAMEOPTION_ROWS)
    1533              :         {
    1534         2964 :             int64       offset = DatumGetInt64(winstate->endOffsetValue);
    1535              : 
    1536              :             /* rows after current row + offset are out of frame */
    1537         2964 :             if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
    1538           57 :                 offset = -offset;
    1539              : 
    1540         2964 :             if (pos > winstate->currentpos + offset)
    1541          603 :                 return -1;
    1542              :         }
    1543         6993 :         else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    1544              :         {
    1545              :             /* hard cases, so delegate to update_frametailpos */
    1546         6993 :             update_frametailpos(winstate);
    1547         6972 :             if (pos >= winstate->frametailpos)
    1548          735 :                 return -1;
    1549              :         }
    1550              :         else
    1551              :             Assert(false);
    1552              :     }
    1553              : 
    1554              :     /* Check exclusion clause */
    1555        93700 :     if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
    1556              :     {
    1557         1473 :         if (pos == winstate->currentpos)
    1558          249 :             return 0;
    1559              :     }
    1560        92227 :     else if ((frameOptions & FRAMEOPTION_EXCLUDE_GROUP) ||
    1561        90796 :              ((frameOptions & FRAMEOPTION_EXCLUDE_TIES) &&
    1562         1485 :               pos != winstate->currentpos))
    1563              :     {
    1564         2646 :         WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    1565              : 
    1566              :         /* If no ORDER BY, all rows are peers with each other */
    1567         2646 :         if (node->ordNumCols == 0)
    1568          234 :             return 0;
    1569              :         /* Otherwise, check the group boundaries */
    1570         2412 :         if (pos >= winstate->groupheadpos)
    1571              :         {
    1572         1296 :             update_grouptailpos(winstate);
    1573         1296 :             if (pos < winstate->grouptailpos)
    1574          504 :                 return 0;
    1575              :         }
    1576              :     }
    1577              : 
    1578              :     /* If we get here, it's in frame */
    1579        92713 :     return 1;
    1580              : }
    1581              : 
    1582              : /*
    1583              :  * update_frameheadpos
    1584              :  * make frameheadpos valid for the current row
    1585              :  *
    1586              :  * Note that frameheadpos is computed without regard for any window exclusion
    1587              :  * clause; the current row and/or its peers are considered part of the frame
    1588              :  * for this purpose even if they must be excluded later.
    1589              :  *
    1590              :  * May clobber winstate->temp_slot_2.
    1591              :  */
    1592              : static void
    1593       182403 : update_frameheadpos(WindowAggState *winstate)
    1594              : {
    1595       182403 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    1596       182403 :     int         frameOptions = winstate->frameOptions;
    1597              :     MemoryContext oldcontext;
    1598              : 
    1599       182403 :     if (winstate->framehead_valid)
    1600        99547 :         return;                 /* already known for current row */
    1601              : 
    1602              :     /* We may be called in a short-lived context */
    1603        82856 :     oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    1604              : 
    1605        82856 :     if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
    1606              :     {
    1607              :         /* In UNBOUNDED PRECEDING mode, frame head is always row 0 */
    1608        77506 :         winstate->frameheadpos = 0;
    1609        77506 :         winstate->framehead_valid = true;
    1610              :     }
    1611         5350 :     else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
    1612              :     {
    1613         1402 :         if (frameOptions & FRAMEOPTION_ROWS)
    1614              :         {
    1615              :             /* In ROWS mode, frame head is the same as current */
    1616         1188 :             winstate->frameheadpos = winstate->currentpos;
    1617         1188 :             winstate->framehead_valid = true;
    1618              :         }
    1619          214 :         else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    1620              :         {
    1621              :             /* If no ORDER BY, all rows are peers with each other */
    1622          214 :             if (node->ordNumCols == 0)
    1623              :             {
    1624            0 :                 winstate->frameheadpos = 0;
    1625            0 :                 winstate->framehead_valid = true;
    1626            0 :                 MemoryContextSwitchTo(oldcontext);
    1627            0 :                 return;
    1628              :             }
    1629              : 
    1630              :             /*
    1631              :              * In RANGE or GROUPS START_CURRENT_ROW mode, frame head is the
    1632              :              * first row that is a peer of current row.  We keep a copy of the
    1633              :              * last-known frame head row in framehead_slot, and advance as
    1634              :              * necessary.  Note that if we reach end of partition, we will
    1635              :              * leave frameheadpos = end+1 and framehead_slot empty.
    1636              :              */
    1637          214 :             tuplestore_select_read_pointer(winstate->buffer,
    1638              :                                            winstate->framehead_ptr);
    1639          214 :             if (winstate->frameheadpos == 0 &&
    1640          106 :                 TupIsNull(winstate->framehead_slot))
    1641              :             {
    1642              :                 /* fetch first row into framehead_slot, if we didn't already */
    1643           41 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1644              :                                              winstate->framehead_slot))
    1645            0 :                     elog(ERROR, "unexpected end of tuplestore");
    1646              :             }
    1647              : 
    1648          372 :             while (!TupIsNull(winstate->framehead_slot))
    1649              :             {
    1650          372 :                 if (are_peers(winstate, winstate->framehead_slot,
    1651              :                               winstate->ss.ss_ScanTupleSlot))
    1652          214 :                     break;      /* this row is the correct frame head */
    1653              :                 /* Note we advance frameheadpos even if the fetch fails */
    1654          158 :                 winstate->frameheadpos++;
    1655          158 :                 spool_tuples(winstate, winstate->frameheadpos);
    1656          158 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1657              :                                              winstate->framehead_slot))
    1658            0 :                     break;      /* end of partition */
    1659              :             }
    1660          214 :             winstate->framehead_valid = true;
    1661              :         }
    1662              :         else
    1663              :             Assert(false);
    1664              :     }
    1665         3948 :     else if (frameOptions & FRAMEOPTION_START_OFFSET)
    1666              :     {
    1667         3948 :         if (frameOptions & FRAMEOPTION_ROWS)
    1668              :         {
    1669              :             /* In ROWS mode, bound is physically n before/after current */
    1670          996 :             int64       offset = DatumGetInt64(winstate->startOffsetValue);
    1671              : 
    1672          996 :             if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
    1673          966 :                 offset = -offset;
    1674              : 
    1675          996 :             winstate->frameheadpos = winstate->currentpos + offset;
    1676              :             /* frame head can't go before first row */
    1677          996 :             if (winstate->frameheadpos < 0)
    1678          168 :                 winstate->frameheadpos = 0;
    1679          828 :             else if (winstate->frameheadpos > winstate->currentpos + 1)
    1680              :             {
    1681              :                 /* make sure frameheadpos is not past end of partition */
    1682            0 :                 spool_tuples(winstate, winstate->frameheadpos - 1);
    1683            0 :                 if (winstate->frameheadpos > winstate->spooled_rows)
    1684            0 :                     winstate->frameheadpos = winstate->spooled_rows;
    1685              :             }
    1686          996 :             winstate->framehead_valid = true;
    1687              :         }
    1688         2952 :         else if (frameOptions & FRAMEOPTION_RANGE)
    1689              :         {
    1690              :             /*
    1691              :              * In RANGE START_OFFSET mode, frame head is the first row that
    1692              :              * satisfies the in_range constraint relative to the current row.
    1693              :              * We keep a copy of the last-known frame head row in
    1694              :              * framehead_slot, and advance as necessary.  Note that if we
    1695              :              * reach end of partition, we will leave frameheadpos = end+1 and
    1696              :              * framehead_slot empty.
    1697              :              */
    1698         2262 :             int         sortCol = node->ordColIdx[0];
    1699              :             bool        sub,
    1700              :                         less;
    1701              : 
    1702              :             /* We must have an ordering column */
    1703              :             Assert(node->ordNumCols == 1);
    1704              : 
    1705              :             /* Precompute flags for in_range checks */
    1706         2262 :             if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
    1707         1851 :                 sub = true;     /* subtract startOffset from current row */
    1708              :             else
    1709          411 :                 sub = false;    /* add it */
    1710         2262 :             less = false;       /* normally, we want frame head >= sum */
    1711              :             /* If sort order is descending, flip both flags */
    1712         2262 :             if (!winstate->inRangeAsc)
    1713              :             {
    1714          327 :                 sub = !sub;
    1715          327 :                 less = true;
    1716              :             }
    1717              : 
    1718         2262 :             tuplestore_select_read_pointer(winstate->buffer,
    1719              :                                            winstate->framehead_ptr);
    1720         2262 :             if (winstate->frameheadpos == 0 &&
    1721         1251 :                 TupIsNull(winstate->framehead_slot))
    1722              :             {
    1723              :                 /* fetch first row into framehead_slot, if we didn't already */
    1724          285 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1725              :                                              winstate->framehead_slot))
    1726            0 :                     elog(ERROR, "unexpected end of tuplestore");
    1727              :             }
    1728              : 
    1729         3633 :             while (!TupIsNull(winstate->framehead_slot))
    1730              :             {
    1731              :                 Datum       headval,
    1732              :                             currval;
    1733              :                 bool        headisnull,
    1734              :                             currisnull;
    1735              : 
    1736         3531 :                 headval = slot_getattr(winstate->framehead_slot, sortCol,
    1737              :                                        &headisnull);
    1738         3531 :                 currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
    1739              :                                        &currisnull);
    1740         3531 :                 if (headisnull || currisnull)
    1741              :                 {
    1742              :                     /* order of the rows depends only on nulls_first */
    1743           54 :                     if (winstate->inRangeNullsFirst)
    1744              :                     {
    1745              :                         /* advance head if head is null and curr is not */
    1746           24 :                         if (!headisnull || currisnull)
    1747              :                             break;
    1748              :                     }
    1749              :                     else
    1750              :                     {
    1751              :                         /* advance head if head is not null and curr is null */
    1752           30 :                         if (headisnull || !currisnull)
    1753              :                             break;
    1754              :                     }
    1755              :                 }
    1756              :                 else
    1757              :                 {
    1758         3477 :                     if (DatumGetBool(FunctionCall5Coll(&winstate->startInRangeFunc,
    1759              :                                                        winstate->inRangeColl,
    1760              :                                                        headval,
    1761              :                                                        currval,
    1762              :                                                        winstate->startOffsetValue,
    1763              :                                                        BoolGetDatum(sub),
    1764              :                                                        BoolGetDatum(less))))
    1765         2085 :                         break;  /* this row is the correct frame head */
    1766              :                 }
    1767              :                 /* Note we advance frameheadpos even if the fetch fails */
    1768         1398 :                 winstate->frameheadpos++;
    1769         1398 :                 spool_tuples(winstate, winstate->frameheadpos);
    1770         1398 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1771              :                                              winstate->framehead_slot))
    1772           27 :                     break;      /* end of partition */
    1773              :             }
    1774         2238 :             winstate->framehead_valid = true;
    1775              :         }
    1776          690 :         else if (frameOptions & FRAMEOPTION_GROUPS)
    1777              :         {
    1778              :             /*
    1779              :              * In GROUPS START_OFFSET mode, frame head is the first row of the
    1780              :              * first peer group whose number satisfies the offset constraint.
    1781              :              * We keep a copy of the last-known frame head row in
    1782              :              * framehead_slot, and advance as necessary.  Note that if we
    1783              :              * reach end of partition, we will leave frameheadpos = end+1 and
    1784              :              * framehead_slot empty.
    1785              :              */
    1786          690 :             int64       offset = DatumGetInt64(winstate->startOffsetValue);
    1787              :             int64       minheadgroup;
    1788              : 
    1789          690 :             if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
    1790          564 :                 minheadgroup = winstate->currentgroup - offset;
    1791              :             else
    1792          126 :                 minheadgroup = winstate->currentgroup + offset;
    1793              : 
    1794          690 :             tuplestore_select_read_pointer(winstate->buffer,
    1795              :                                            winstate->framehead_ptr);
    1796          690 :             if (winstate->frameheadpos == 0 &&
    1797          375 :                 TupIsNull(winstate->framehead_slot))
    1798              :             {
    1799              :                 /* fetch first row into framehead_slot, if we didn't already */
    1800          186 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1801              :                                              winstate->framehead_slot))
    1802            0 :                     elog(ERROR, "unexpected end of tuplestore");
    1803              :             }
    1804              : 
    1805         1761 :             while (!TupIsNull(winstate->framehead_slot))
    1806              :             {
    1807         1059 :                 if (winstate->frameheadgroup >= minheadgroup)
    1808          660 :                     break;      /* this row is the correct frame head */
    1809          399 :                 ExecCopySlot(winstate->temp_slot_2, winstate->framehead_slot);
    1810              :                 /* Note we advance frameheadpos even if the fetch fails */
    1811          399 :                 winstate->frameheadpos++;
    1812          399 :                 spool_tuples(winstate, winstate->frameheadpos);
    1813          399 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1814              :                                              winstate->framehead_slot))
    1815           18 :                     break;      /* end of partition */
    1816          381 :                 if (!are_peers(winstate, winstate->temp_slot_2,
    1817              :                                winstate->framehead_slot))
    1818          261 :                     winstate->frameheadgroup++;
    1819              :             }
    1820          690 :             ExecClearTuple(winstate->temp_slot_2);
    1821          690 :             winstate->framehead_valid = true;
    1822              :         }
    1823              :         else
    1824              :             Assert(false);
    1825              :     }
    1826              :     else
    1827              :         Assert(false);
    1828              : 
    1829        82832 :     MemoryContextSwitchTo(oldcontext);
    1830              : }
    1831              : 
    1832              : /*
    1833              :  * update_frametailpos
    1834              :  * make frametailpos valid for the current row
    1835              :  *
    1836              :  * Note that frametailpos is computed without regard for any window exclusion
    1837              :  * clause; the current row and/or its peers are considered part of the frame
    1838              :  * for this purpose even if they must be excluded later.
    1839              :  *
    1840              :  * May clobber winstate->temp_slot_2.
    1841              :  */
    1842              : static void
    1843       101431 : update_frametailpos(WindowAggState *winstate)
    1844              : {
    1845       101431 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    1846       101431 :     int         frameOptions = winstate->frameOptions;
    1847              :     MemoryContext oldcontext;
    1848              : 
    1849       101431 :     if (winstate->frametail_valid)
    1850         8970 :         return;                 /* already known for current row */
    1851              : 
    1852              :     /* We may be called in a short-lived context */
    1853        92461 :     oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    1854              : 
    1855        92461 :     if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
    1856              :     {
    1857              :         /* In UNBOUNDED FOLLOWING mode, all partition rows are in frame */
    1858          120 :         spool_tuples(winstate, -1);
    1859          120 :         winstate->frametailpos = winstate->spooled_rows;
    1860          120 :         winstate->frametail_valid = true;
    1861              :     }
    1862        92341 :     else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
    1863              :     {
    1864        89029 :         if (frameOptions & FRAMEOPTION_ROWS)
    1865              :         {
    1866              :             /* In ROWS mode, exactly the rows up to current are in frame */
    1867           60 :             winstate->frametailpos = winstate->currentpos + 1;
    1868           60 :             winstate->frametail_valid = true;
    1869              :         }
    1870        88969 :         else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    1871              :         {
    1872              :             /* If no ORDER BY, all rows are peers with each other */
    1873        88969 :             if (node->ordNumCols == 0)
    1874              :             {
    1875           30 :                 spool_tuples(winstate, -1);
    1876           30 :                 winstate->frametailpos = winstate->spooled_rows;
    1877           30 :                 winstate->frametail_valid = true;
    1878           30 :                 MemoryContextSwitchTo(oldcontext);
    1879           30 :                 return;
    1880              :             }
    1881              : 
    1882              :             /*
    1883              :              * In RANGE or GROUPS END_CURRENT_ROW mode, frame end is the last
    1884              :              * row that is a peer of current row, frame tail is the row after
    1885              :              * that (if any).  We keep a copy of the last-known frame tail row
    1886              :              * in frametail_slot, and advance as necessary.  Note that if we
    1887              :              * reach end of partition, we will leave frametailpos = end+1 and
    1888              :              * frametail_slot empty.
    1889              :              */
    1890        88939 :             tuplestore_select_read_pointer(winstate->buffer,
    1891              :                                            winstate->frametail_ptr);
    1892        88939 :             if (winstate->frametailpos == 0 &&
    1893          353 :                 TupIsNull(winstate->frametail_slot))
    1894              :             {
    1895              :                 /* fetch first row into frametail_slot, if we didn't already */
    1896          353 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1897              :                                              winstate->frametail_slot))
    1898            0 :                     elog(ERROR, "unexpected end of tuplestore");
    1899              :             }
    1900              : 
    1901       177531 :             while (!TupIsNull(winstate->frametail_slot))
    1902              :             {
    1903       165471 :                 if (winstate->frametailpos > winstate->currentpos &&
    1904       136712 :                     !are_peers(winstate, winstate->frametail_slot,
    1905              :                                winstate->ss.ss_ScanTupleSlot))
    1906        76532 :                     break;      /* this row is the frame tail */
    1907              :                 /* Note we advance frametailpos even if the fetch fails */
    1908        88939 :                 winstate->frametailpos++;
    1909        88939 :                 spool_tuples(winstate, winstate->frametailpos);
    1910        88939 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1911              :                                              winstate->frametail_slot))
    1912          347 :                     break;      /* end of partition */
    1913              :             }
    1914        88939 :             winstate->frametail_valid = true;
    1915              :         }
    1916              :         else
    1917              :             Assert(false);
    1918              :     }
    1919         3312 :     else if (frameOptions & FRAMEOPTION_END_OFFSET)
    1920              :     {
    1921         3312 :         if (frameOptions & FRAMEOPTION_ROWS)
    1922              :         {
    1923              :             /* In ROWS mode, bound is physically n before/after current */
    1924          210 :             int64       offset = DatumGetInt64(winstate->endOffsetValue);
    1925              : 
    1926          210 :             if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
    1927            0 :                 offset = -offset;
    1928              : 
    1929          210 :             winstate->frametailpos = winstate->currentpos + offset + 1;
    1930              :             /* smallest allowable value of frametailpos is 0 */
    1931          210 :             if (winstate->frametailpos < 0)
    1932            0 :                 winstate->frametailpos = 0;
    1933          210 :             else if (winstate->frametailpos > winstate->currentpos + 1)
    1934              :             {
    1935              :                 /* make sure frametailpos is not past end of partition */
    1936          210 :                 spool_tuples(winstate, winstate->frametailpos - 1);
    1937          210 :                 if (winstate->frametailpos > winstate->spooled_rows)
    1938           48 :                     winstate->frametailpos = winstate->spooled_rows;
    1939              :             }
    1940          210 :             winstate->frametail_valid = true;
    1941              :         }
    1942         3102 :         else if (frameOptions & FRAMEOPTION_RANGE)
    1943              :         {
    1944              :             /*
    1945              :              * In RANGE END_OFFSET mode, frame end is the last row that
    1946              :              * satisfies the in_range constraint relative to the current row,
    1947              :              * frame tail is the row after that (if any).  We keep a copy of
    1948              :              * the last-known frame tail row in frametail_slot, and advance as
    1949              :              * necessary.  Note that if we reach end of partition, we will
    1950              :              * leave frametailpos = end+1 and frametail_slot empty.
    1951              :              */
    1952         2442 :             int         sortCol = node->ordColIdx[0];
    1953              :             bool        sub,
    1954              :                         less;
    1955              : 
    1956              :             /* We must have an ordering column */
    1957              :             Assert(node->ordNumCols == 1);
    1958              : 
    1959              :             /* Precompute flags for in_range checks */
    1960         2442 :             if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
    1961          456 :                 sub = true;     /* subtract endOffset from current row */
    1962              :             else
    1963         1986 :                 sub = false;    /* add it */
    1964         2442 :             less = true;        /* normally, we want frame tail <= sum */
    1965              :             /* If sort order is descending, flip both flags */
    1966         2442 :             if (!winstate->inRangeAsc)
    1967              :             {
    1968          345 :                 sub = !sub;
    1969          345 :                 less = false;
    1970              :             }
    1971              : 
    1972         2442 :             tuplestore_select_read_pointer(winstate->buffer,
    1973              :                                            winstate->frametail_ptr);
    1974         2442 :             if (winstate->frametailpos == 0 &&
    1975          411 :                 TupIsNull(winstate->frametail_slot))
    1976              :             {
    1977              :                 /* fetch first row into frametail_slot, if we didn't already */
    1978          294 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    1979              :                                              winstate->frametail_slot))
    1980            0 :                     elog(ERROR, "unexpected end of tuplestore");
    1981              :             }
    1982              : 
    1983         4503 :             while (!TupIsNull(winstate->frametail_slot))
    1984              :             {
    1985              :                 Datum       tailval,
    1986              :                             currval;
    1987              :                 bool        tailisnull,
    1988              :                             currisnull;
    1989              : 
    1990         3720 :                 tailval = slot_getattr(winstate->frametail_slot, sortCol,
    1991              :                                        &tailisnull);
    1992         3720 :                 currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, sortCol,
    1993              :                                        &currisnull);
    1994         3720 :                 if (tailisnull || currisnull)
    1995              :                 {
    1996              :                     /* order of the rows depends only on nulls_first */
    1997           54 :                     if (winstate->inRangeNullsFirst)
    1998              :                     {
    1999              :                         /* advance tail if tail is null or curr is not */
    2000           24 :                         if (!tailisnull)
    2001         1635 :                             break;
    2002              :                     }
    2003              :                     else
    2004              :                     {
    2005              :                         /* advance tail if tail is not null or curr is null */
    2006           30 :                         if (!currisnull)
    2007           18 :                             break;
    2008              :                     }
    2009              :                 }
    2010              :                 else
    2011              :                 {
    2012         3666 :                     if (!DatumGetBool(FunctionCall5Coll(&winstate->endInRangeFunc,
    2013              :                                                         winstate->inRangeColl,
    2014              :                                                         tailval,
    2015              :                                                         currval,
    2016              :                                                         winstate->endOffsetValue,
    2017              :                                                         BoolGetDatum(sub),
    2018              :                                                         BoolGetDatum(less))))
    2019         1365 :                         break;  /* this row is the correct frame tail */
    2020              :                 }
    2021              :                 /* Note we advance frametailpos even if the fetch fails */
    2022         2301 :                 winstate->frametailpos++;
    2023         2301 :                 spool_tuples(winstate, winstate->frametailpos);
    2024         2301 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2025              :                                              winstate->frametail_slot))
    2026          240 :                     break;      /* end of partition */
    2027              :             }
    2028         2418 :             winstate->frametail_valid = true;
    2029              :         }
    2030          660 :         else if (frameOptions & FRAMEOPTION_GROUPS)
    2031              :         {
    2032              :             /*
    2033              :              * In GROUPS END_OFFSET mode, frame end is the last row of the
    2034              :              * last peer group whose number satisfies the offset constraint,
    2035              :              * and frame tail is the row after that (if any).  We keep a copy
    2036              :              * of the last-known frame tail row in frametail_slot, and advance
    2037              :              * as necessary.  Note that if we reach end of partition, we will
    2038              :              * leave frametailpos = end+1 and frametail_slot empty.
    2039              :              */
    2040          660 :             int64       offset = DatumGetInt64(winstate->endOffsetValue);
    2041              :             int64       maxtailgroup;
    2042              : 
    2043          660 :             if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
    2044           36 :                 maxtailgroup = winstate->currentgroup - offset;
    2045              :             else
    2046          624 :                 maxtailgroup = winstate->currentgroup + offset;
    2047              : 
    2048          660 :             tuplestore_select_read_pointer(winstate->buffer,
    2049              :                                            winstate->frametail_ptr);
    2050          660 :             if (winstate->frametailpos == 0 &&
    2051          192 :                 TupIsNull(winstate->frametail_slot))
    2052              :             {
    2053              :                 /* fetch first row into frametail_slot, if we didn't already */
    2054          183 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2055              :                                              winstate->frametail_slot))
    2056            0 :                     elog(ERROR, "unexpected end of tuplestore");
    2057              :             }
    2058              : 
    2059         1794 :             while (!TupIsNull(winstate->frametail_slot))
    2060              :             {
    2061         1020 :                 if (winstate->frametailgroup > maxtailgroup)
    2062          372 :                     break;      /* this row is the correct frame tail */
    2063          648 :                 ExecCopySlot(winstate->temp_slot_2, winstate->frametail_slot);
    2064              :                 /* Note we advance frametailpos even if the fetch fails */
    2065          648 :                 winstate->frametailpos++;
    2066          648 :                 spool_tuples(winstate, winstate->frametailpos);
    2067          648 :                 if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2068              :                                              winstate->frametail_slot))
    2069          174 :                     break;      /* end of partition */
    2070          474 :                 if (!are_peers(winstate, winstate->temp_slot_2,
    2071              :                                winstate->frametail_slot))
    2072          300 :                     winstate->frametailgroup++;
    2073              :             }
    2074          660 :             ExecClearTuple(winstate->temp_slot_2);
    2075          660 :             winstate->frametail_valid = true;
    2076              :         }
    2077              :         else
    2078              :             Assert(false);
    2079              :     }
    2080              :     else
    2081              :         Assert(false);
    2082              : 
    2083        92407 :     MemoryContextSwitchTo(oldcontext);
    2084              : }
    2085              : 
    2086              : /*
    2087              :  * update_grouptailpos
    2088              :  * make grouptailpos valid for the current row
    2089              :  *
    2090              :  * May clobber winstate->temp_slot_2.
    2091              :  */
    2092              : static void
    2093         2436 : update_grouptailpos(WindowAggState *winstate)
    2094              : {
    2095         2436 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    2096              :     MemoryContext oldcontext;
    2097              : 
    2098         2436 :     if (winstate->grouptail_valid)
    2099         1977 :         return;                 /* already known for current row */
    2100              : 
    2101              :     /* We may be called in a short-lived context */
    2102          459 :     oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    2103              : 
    2104              :     /* If no ORDER BY, all rows are peers with each other */
    2105          459 :     if (node->ordNumCols == 0)
    2106              :     {
    2107            0 :         spool_tuples(winstate, -1);
    2108            0 :         winstate->grouptailpos = winstate->spooled_rows;
    2109            0 :         winstate->grouptail_valid = true;
    2110            0 :         MemoryContextSwitchTo(oldcontext);
    2111            0 :         return;
    2112              :     }
    2113              : 
    2114              :     /*
    2115              :      * Because grouptail_valid is reset only when current row advances into a
    2116              :      * new peer group, we always reach here knowing that grouptailpos needs to
    2117              :      * be advanced by at least one row.  Hence, unlike the otherwise similar
    2118              :      * case for frame tail tracking, we do not need persistent storage of the
    2119              :      * group tail row.
    2120              :      */
    2121              :     Assert(winstate->grouptailpos <= winstate->currentpos);
    2122          459 :     tuplestore_select_read_pointer(winstate->buffer,
    2123              :                                    winstate->grouptail_ptr);
    2124              :     for (;;)
    2125              :     {
    2126              :         /* Note we advance grouptailpos even if the fetch fails */
    2127          879 :         winstate->grouptailpos++;
    2128          879 :         spool_tuples(winstate, winstate->grouptailpos);
    2129          879 :         if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2130              :                                      winstate->temp_slot_2))
    2131          129 :             break;              /* end of partition */
    2132          750 :         if (winstate->grouptailpos > winstate->currentpos &&
    2133          621 :             !are_peers(winstate, winstate->temp_slot_2,
    2134              :                        winstate->ss.ss_ScanTupleSlot))
    2135          330 :             break;              /* this row is the group tail */
    2136              :     }
    2137          459 :     ExecClearTuple(winstate->temp_slot_2);
    2138          459 :     winstate->grouptail_valid = true;
    2139              : 
    2140          459 :     MemoryContextSwitchTo(oldcontext);
    2141              : }
    2142              : 
    2143              : /*
    2144              :  * calculate_frame_offsets
    2145              :  *      Determine the startOffsetValue and endOffsetValue values for the
    2146              :  *      WindowAgg's frame options.
    2147              :  */
    2148              : static pg_noinline void
    2149         1191 : calculate_frame_offsets(PlanState *pstate)
    2150              : {
    2151         1191 :     WindowAggState *winstate = castNode(WindowAggState, pstate);
    2152              :     ExprContext *econtext;
    2153         1191 :     int         frameOptions = winstate->frameOptions;
    2154              :     Datum       value;
    2155              :     bool        isnull;
    2156              :     int16       len;
    2157              :     bool        byval;
    2158              : 
    2159              :     /* Ensure we've not been called before for this scan */
    2160              :     Assert(winstate->all_first);
    2161              : 
    2162         1191 :     econtext = winstate->ss.ps.ps_ExprContext;
    2163              : 
    2164         1191 :     if (frameOptions & FRAMEOPTION_START_OFFSET)
    2165              :     {
    2166              :         Assert(winstate->startOffset != NULL);
    2167          432 :         value = ExecEvalExprSwitchContext(winstate->startOffset,
    2168              :                                           econtext,
    2169              :                                           &isnull);
    2170          432 :         if (isnull)
    2171            0 :             ereport(ERROR,
    2172              :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    2173              :                      errmsg("frame starting offset must not be null")));
    2174              :         /* copy value into query-lifespan context */
    2175          432 :         get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
    2176              :                         &len,
    2177              :                         &byval);
    2178          432 :         winstate->startOffsetValue = datumCopy(value, byval, len);
    2179          432 :         if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
    2180              :         {
    2181              :             /* value is known to be int8 */
    2182          174 :             int64       offset = DatumGetInt64(value);
    2183              : 
    2184          174 :             if (offset < 0)
    2185            0 :                 ereport(ERROR,
    2186              :                         (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
    2187              :                          errmsg("frame starting offset must not be negative")));
    2188              :         }
    2189              :     }
    2190              : 
    2191         1191 :     if (frameOptions & FRAMEOPTION_END_OFFSET)
    2192              :     {
    2193              :         Assert(winstate->endOffset != NULL);
    2194          480 :         value = ExecEvalExprSwitchContext(winstate->endOffset,
    2195              :                                           econtext,
    2196              :                                           &isnull);
    2197          480 :         if (isnull)
    2198            0 :             ereport(ERROR,
    2199              :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    2200              :                      errmsg("frame ending offset must not be null")));
    2201              :         /* copy value into query-lifespan context */
    2202          480 :         get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
    2203              :                         &len,
    2204              :                         &byval);
    2205          480 :         winstate->endOffsetValue = datumCopy(value, byval, len);
    2206          480 :         if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
    2207              :         {
    2208              :             /* value is known to be int8 */
    2209          189 :             int64       offset = DatumGetInt64(value);
    2210              : 
    2211          189 :             if (offset < 0)
    2212            0 :                 ereport(ERROR,
    2213              :                         (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
    2214              :                          errmsg("frame ending offset must not be negative")));
    2215              :         }
    2216              :     }
    2217         1191 :     winstate->all_first = false;
    2218         1191 : }
    2219              : 
    2220              : /* -----------------
    2221              :  * ExecWindowAgg
    2222              :  *
    2223              :  *  ExecWindowAgg receives tuples from its outer subplan and
    2224              :  *  stores them into a tuplestore, then processes window functions.
    2225              :  *  This node doesn't reduce nor qualify any row so the number of
    2226              :  *  returned rows is exactly the same as its outer subplan's result.
    2227              :  * -----------------
    2228              :  */
    2229              : static TupleTableSlot *
    2230       453643 : ExecWindowAgg(PlanState *pstate)
    2231              : {
    2232       453643 :     WindowAggState *winstate = castNode(WindowAggState, pstate);
    2233              :     TupleTableSlot *slot;
    2234              :     ExprContext *econtext;
    2235              :     int         i;
    2236              :     int         numfuncs;
    2237              : 
    2238       453643 :     CHECK_FOR_INTERRUPTS();
    2239              : 
    2240       453643 :     if (winstate->status == WINDOWAGG_DONE)
    2241            0 :         return NULL;
    2242              : 
    2243              :     /*
    2244              :      * Compute frame offset values, if any, during first call (or after a
    2245              :      * rescan).  These are assumed to hold constant throughout the scan; if
    2246              :      * user gives us a volatile expression, we'll only use its initial value.
    2247              :      */
    2248       453643 :     if (unlikely(winstate->all_first))
    2249         1191 :         calculate_frame_offsets(pstate);
    2250              : 
    2251              :     /* We need to loop as the runCondition or qual may filter out tuples */
    2252              :     for (;;)
    2253              :     {
    2254       453709 :         if (winstate->next_partition)
    2255              :         {
    2256              :             /* Initialize for first partition and set current row = 0 */
    2257         1191 :             begin_partition(winstate);
    2258              :             /* If there are no input rows, we'll detect that and exit below */
    2259              :         }
    2260              :         else
    2261              :         {
    2262              :             /* Advance current row within partition */
    2263       452518 :             winstate->currentpos++;
    2264              :             /* This might mean that the frame moves, too */
    2265       452518 :             winstate->framehead_valid = false;
    2266       452518 :             winstate->frametail_valid = false;
    2267              :             /* we don't need to invalidate grouptail here; see below */
    2268              :         }
    2269              : 
    2270              :         /*
    2271              :          * Spool all tuples up to and including the current row, if we haven't
    2272              :          * already
    2273              :          */
    2274       453709 :         spool_tuples(winstate, winstate->currentpos);
    2275              : 
    2276              :         /* Move to the next partition if we reached the end of this partition */
    2277       453709 :         if (winstate->partition_spooled &&
    2278        31520 :             winstate->currentpos >= winstate->spooled_rows)
    2279              :         {
    2280         1687 :             release_partition(winstate);
    2281              : 
    2282         1687 :             if (winstate->more_partitions)
    2283              :             {
    2284          622 :                 begin_partition(winstate);
    2285              :                 Assert(winstate->spooled_rows > 0);
    2286              : 
    2287              :                 /* Come out of pass-through mode when changing partition */
    2288          622 :                 winstate->status = WINDOWAGG_RUN;
    2289              :             }
    2290              :             else
    2291              :             {
    2292              :                 /* No further partitions?  We're done */
    2293         1065 :                 winstate->status = WINDOWAGG_DONE;
    2294         1065 :                 return NULL;
    2295              :             }
    2296              :         }
    2297              : 
    2298              :         /* final output execution is in ps_ExprContext */
    2299       452644 :         econtext = winstate->ss.ps.ps_ExprContext;
    2300              : 
    2301              :         /* Clear the per-output-tuple context for current row */
    2302       452644 :         ResetExprContext(econtext);
    2303              : 
    2304              :         /*
    2305              :          * Read the current row from the tuplestore, and save in
    2306              :          * ScanTupleSlot. (We can't rely on the outerplan's output slot
    2307              :          * because we may have to read beyond the current row.  Also, we have
    2308              :          * to actually copy the row out of the tuplestore, since window
    2309              :          * function evaluation might cause the tuplestore to dump its state to
    2310              :          * disk.)
    2311              :          *
    2312              :          * In GROUPS mode, or when tracking a group-oriented exclusion clause,
    2313              :          * we must also detect entering a new peer group and update associated
    2314              :          * state when that happens.  We use temp_slot_2 to temporarily hold
    2315              :          * the previous row for this purpose.
    2316              :          *
    2317              :          * Current row must be in the tuplestore, since we spooled it above.
    2318              :          */
    2319       452644 :         tuplestore_select_read_pointer(winstate->buffer, winstate->current_ptr);
    2320       452644 :         if ((winstate->frameOptions & (FRAMEOPTION_GROUPS |
    2321              :                                        FRAMEOPTION_EXCLUDE_GROUP |
    2322         1449 :                                        FRAMEOPTION_EXCLUDE_TIES)) &&
    2323         1449 :             winstate->currentpos > 0)
    2324              :         {
    2325         1179 :             ExecCopySlot(winstate->temp_slot_2, winstate->ss.ss_ScanTupleSlot);
    2326         1179 :             if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2327              :                                          winstate->ss.ss_ScanTupleSlot))
    2328            0 :                 elog(ERROR, "unexpected end of tuplestore");
    2329         1179 :             if (!are_peers(winstate, winstate->temp_slot_2,
    2330              :                            winstate->ss.ss_ScanTupleSlot))
    2331              :             {
    2332          621 :                 winstate->currentgroup++;
    2333          621 :                 winstate->groupheadpos = winstate->currentpos;
    2334          621 :                 winstate->grouptail_valid = false;
    2335              :             }
    2336         1179 :             ExecClearTuple(winstate->temp_slot_2);
    2337              :         }
    2338              :         else
    2339              :         {
    2340       451465 :             if (!tuplestore_gettupleslot(winstate->buffer, true, true,
    2341              :                                          winstate->ss.ss_ScanTupleSlot))
    2342            0 :                 elog(ERROR, "unexpected end of tuplestore");
    2343              :         }
    2344              : 
    2345              :         /* don't evaluate the window functions when we're in pass-through mode */
    2346       452644 :         if (winstate->status == WINDOWAGG_RUN)
    2347              :         {
    2348              :             /*
    2349              :              * Evaluate true window functions
    2350              :              */
    2351       452611 :             numfuncs = winstate->numfuncs;
    2352       969080 :             for (i = 0; i < numfuncs; i++)
    2353              :             {
    2354       516550 :                 WindowStatePerFunc perfuncstate = &(winstate->perfunc[i]);
    2355              : 
    2356       516550 :                 if (perfuncstate->plain_agg)
    2357        81086 :                     continue;
    2358       435464 :                 eval_windowfunction(winstate, perfuncstate,
    2359       435464 :                                     &(econtext->ecxt_aggvalues[perfuncstate->wfuncstate->wfuncno]),
    2360       435464 :                                     &(econtext->ecxt_aggnulls[perfuncstate->wfuncstate->wfuncno]));
    2361              :             }
    2362              : 
    2363              :             /*
    2364              :              * Evaluate aggregates
    2365              :              */
    2366       452530 :             if (winstate->numaggs > 0)
    2367        80189 :                 eval_windowaggregates(winstate);
    2368              :         }
    2369              : 
    2370              :         /*
    2371              :          * If we have created auxiliary read pointers for the frame or group
    2372              :          * boundaries, force them to be kept up-to-date, because we don't know
    2373              :          * whether the window function(s) will do anything that requires that.
    2374              :          * Failing to advance the pointers would result in being unable to
    2375              :          * trim data from the tuplestore, which is bad.  (If we could know in
    2376              :          * advance whether the window functions will use frame boundary info,
    2377              :          * we could skip creating these pointers in the first place ... but
    2378              :          * unfortunately the window function API doesn't require that.)
    2379              :          */
    2380       452542 :         if (winstate->framehead_ptr >= 0)
    2381         3118 :             update_frameheadpos(winstate);
    2382       452542 :         if (winstate->frametail_ptr >= 0)
    2383        92017 :             update_frametailpos(winstate);
    2384       452542 :         if (winstate->grouptail_ptr >= 0)
    2385          750 :             update_grouptailpos(winstate);
    2386              : 
    2387              :         /*
    2388              :          * Truncate any no-longer-needed rows from the tuplestore.
    2389              :          */
    2390       452542 :         tuplestore_trim(winstate->buffer);
    2391              : 
    2392              :         /*
    2393              :          * Form and return a projection tuple using the windowfunc results and
    2394              :          * the current row.  Setting ecxt_outertuple arranges that any Vars
    2395              :          * will be evaluated with respect to that row.
    2396              :          */
    2397       452542 :         econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
    2398              : 
    2399       452542 :         slot = ExecProject(winstate->ss.ps.ps_ProjInfo);
    2400              : 
    2401       452542 :         if (winstate->status == WINDOWAGG_RUN)
    2402              :         {
    2403       452509 :             econtext->ecxt_scantuple = slot;
    2404              : 
    2405              :             /*
    2406              :              * Now evaluate the run condition to see if we need to go into
    2407              :              * pass-through mode, or maybe stop completely.
    2408              :              */
    2409       452509 :             if (!ExecQual(winstate->runcondition, econtext))
    2410              :             {
    2411              :                 /*
    2412              :                  * Determine which mode to move into.  If there is no
    2413              :                  * PARTITION BY clause and we're the top-level WindowAgg then
    2414              :                  * we're done.  This tuple and any future tuples cannot
    2415              :                  * possibly match the runcondition.  However, when there is a
    2416              :                  * PARTITION BY clause or we're not the top-level window we
    2417              :                  * can't just stop as we need to either process other
    2418              :                  * partitions or ensure WindowAgg nodes above us receive all
    2419              :                  * of the tuples they need to process their WindowFuncs.
    2420              :                  */
    2421           66 :                 if (winstate->use_pass_through)
    2422              :                 {
    2423              :                     /*
    2424              :                      * When switching into a pass-through mode, we'd better
    2425              :                      * NULLify the aggregate results as these are no longer
    2426              :                      * updated and NULLifying them avoids the old stale
    2427              :                      * results lingering.  Some of these might be byref types
    2428              :                      * so we can't have them pointing to free'd memory.  The
    2429              :                      * planner insisted that quals used in the runcondition
    2430              :                      * are strict, so the top-level WindowAgg will always
    2431              :                      * filter these NULLs out in the filter clause.
    2432              :                      */
    2433           45 :                     numfuncs = winstate->numfuncs;
    2434          132 :                     for (i = 0; i < numfuncs; i++)
    2435              :                     {
    2436           87 :                         econtext->ecxt_aggvalues[i] = (Datum) 0;
    2437           87 :                         econtext->ecxt_aggnulls[i] = true;
    2438              :                     }
    2439              : 
    2440              :                     /*
    2441              :                      * STRICT pass-through mode is required for the top window
    2442              :                      * when there is a PARTITION BY clause.  Otherwise we must
    2443              :                      * ensure we store tuples that don't match the
    2444              :                      * runcondition so they're available to WindowAggs above.
    2445              :                      */
    2446           45 :                     if (winstate->top_window)
    2447              :                     {
    2448           36 :                         winstate->status = WINDOWAGG_PASSTHROUGH_STRICT;
    2449           36 :                         continue;
    2450              :                     }
    2451              :                     else
    2452              :                     {
    2453            9 :                         winstate->status = WINDOWAGG_PASSTHROUGH;
    2454              :                     }
    2455              :                 }
    2456              :                 else
    2457              :                 {
    2458              :                     /*
    2459              :                      * Pass-through not required.  We can just return NULL.
    2460              :                      * Nothing else will match the runcondition.
    2461              :                      */
    2462           21 :                     winstate->status = WINDOWAGG_DONE;
    2463           21 :                     return NULL;
    2464              :                 }
    2465              :             }
    2466              : 
    2467              :             /*
    2468              :              * Filter out any tuples we don't need in the top-level WindowAgg.
    2469              :              */
    2470       452452 :             if (!ExecQual(winstate->ss.ps.qual, econtext))
    2471              :             {
    2472            9 :                 InstrCountFiltered1(winstate, 1);
    2473            9 :                 continue;
    2474              :             }
    2475              : 
    2476       452443 :             break;
    2477              :         }
    2478              : 
    2479              :         /*
    2480              :          * When not in WINDOWAGG_RUN mode, we must still return this tuple if
    2481              :          * we're anything apart from the top window.
    2482              :          */
    2483           33 :         else if (!winstate->top_window)
    2484           12 :             break;
    2485              :     }
    2486              : 
    2487       452455 :     return slot;
    2488              : }
    2489              : 
    2490              : /* -----------------
    2491              :  * ExecInitWindowAgg
    2492              :  *
    2493              :  *  Creates the run-time information for the WindowAgg node produced by the
    2494              :  *  planner and initializes its outer subtree
    2495              :  * -----------------
    2496              :  */
    2497              : WindowAggState *
    2498         1431 : ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
    2499              : {
    2500              :     WindowAggState *winstate;
    2501              :     Plan       *outerPlan;
    2502              :     ExprContext *econtext;
    2503              :     ExprContext *tmpcontext;
    2504              :     WindowStatePerFunc perfunc;
    2505              :     WindowStatePerAgg peragg;
    2506         1431 :     int         frameOptions = node->frameOptions;
    2507              :     int         numfuncs,
    2508              :                 wfuncno,
    2509              :                 numaggs,
    2510              :                 aggno;
    2511              :     TupleDesc   scanDesc;
    2512              :     ListCell   *l;
    2513              : 
    2514              :     /* check for unsupported flags */
    2515              :     Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
    2516              : 
    2517              :     /*
    2518              :      * create state structure
    2519              :      */
    2520         1431 :     winstate = makeNode(WindowAggState);
    2521         1431 :     winstate->ss.ps.plan = (Plan *) node;
    2522         1431 :     winstate->ss.ps.state = estate;
    2523         1431 :     winstate->ss.ps.ExecProcNode = ExecWindowAgg;
    2524              : 
    2525              :     /* copy frame options to state node for easy access */
    2526         1431 :     winstate->frameOptions = frameOptions;
    2527              : 
    2528              :     /*
    2529              :      * Create expression contexts.  We need two, one for per-input-tuple
    2530              :      * processing and one for per-output-tuple processing.  We cheat a little
    2531              :      * by using ExecAssignExprContext() to build both.
    2532              :      */
    2533         1431 :     ExecAssignExprContext(estate, &winstate->ss.ps);
    2534         1431 :     tmpcontext = winstate->ss.ps.ps_ExprContext;
    2535         1431 :     winstate->tmpcontext = tmpcontext;
    2536         1431 :     ExecAssignExprContext(estate, &winstate->ss.ps);
    2537              : 
    2538              :     /* Create long-lived context for storage of partition-local memory etc */
    2539         1431 :     winstate->partcontext =
    2540         1431 :         AllocSetContextCreate(CurrentMemoryContext,
    2541              :                               "WindowAgg Partition",
    2542              :                               ALLOCSET_DEFAULT_SIZES);
    2543              : 
    2544              :     /*
    2545              :      * Create mid-lived context for aggregate trans values etc.
    2546              :      *
    2547              :      * Note that moving aggregates each use their own private context, not
    2548              :      * this one.
    2549              :      */
    2550         1431 :     winstate->aggcontext =
    2551         1431 :         AllocSetContextCreate(CurrentMemoryContext,
    2552              :                               "WindowAgg Aggregates",
    2553              :                               ALLOCSET_DEFAULT_SIZES);
    2554              : 
    2555              :     /* Only the top-level WindowAgg may have a qual */
    2556              :     Assert(node->plan.qual == NIL || node->topWindow);
    2557              : 
    2558              :     /* Initialize the qual */
    2559         1431 :     winstate->ss.ps.qual = ExecInitQual(node->plan.qual,
    2560              :                                         (PlanState *) winstate);
    2561              : 
    2562              :     /*
    2563              :      * Setup the run condition, if we received one from the query planner.
    2564              :      * When set, this may allow us to move into pass-through mode so that we
    2565              :      * don't have to perform any further evaluation of WindowFuncs in the
    2566              :      * current partition or possibly stop returning tuples altogether when all
    2567              :      * tuples are in the same partition.
    2568              :      */
    2569         1431 :     winstate->runcondition = ExecInitQual(node->runCondition,
    2570              :                                           (PlanState *) winstate);
    2571              : 
    2572              :     /*
    2573              :      * When we're not the top-level WindowAgg node or we are but have a
    2574              :      * PARTITION BY clause we must move into one of the WINDOWAGG_PASSTHROUGH*
    2575              :      * modes when the runCondition becomes false.
    2576              :      */
    2577         1431 :     winstate->use_pass_through = !node->topWindow || node->partNumCols > 0;
    2578              : 
    2579              :     /* remember if we're the top-window or we are below the top-window */
    2580         1431 :     winstate->top_window = node->topWindow;
    2581              : 
    2582              :     /*
    2583              :      * initialize child nodes
    2584              :      */
    2585         1431 :     outerPlan = outerPlan(node);
    2586         1431 :     outerPlanState(winstate) = ExecInitNode(outerPlan, estate, eflags);
    2587              : 
    2588              :     /*
    2589              :      * initialize source tuple type (which is also the tuple type that we'll
    2590              :      * store in the tuplestore and use in all our working slots).
    2591              :      */
    2592         1431 :     ExecCreateScanSlotFromOuterPlan(estate, &winstate->ss, &TTSOpsMinimalTuple);
    2593         1431 :     scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
    2594              : 
    2595              :     /* the outer tuple isn't the child's tuple, but always a minimal tuple */
    2596         1431 :     winstate->ss.ps.outeropsset = true;
    2597         1431 :     winstate->ss.ps.outerops = &TTSOpsMinimalTuple;
    2598         1431 :     winstate->ss.ps.outeropsfixed = true;
    2599              : 
    2600              :     /*
    2601              :      * tuple table initialization
    2602              :      */
    2603         1431 :     winstate->first_part_slot = ExecInitExtraTupleSlot(estate, scanDesc,
    2604              :                                                        &TTSOpsMinimalTuple);
    2605         1431 :     winstate->agg_row_slot = ExecInitExtraTupleSlot(estate, scanDesc,
    2606              :                                                     &TTSOpsMinimalTuple);
    2607         1431 :     winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate, scanDesc,
    2608              :                                                    &TTSOpsMinimalTuple);
    2609         1431 :     winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
    2610              :                                                    &TTSOpsMinimalTuple);
    2611              : 
    2612              :     /*
    2613              :      * create frame head and tail slots only if needed (must create slots in
    2614              :      * exactly the same cases that update_frameheadpos and update_frametailpos
    2615              :      * need them)
    2616              :      */
    2617         1431 :     winstate->framehead_slot = winstate->frametail_slot = NULL;
    2618              : 
    2619         1431 :     if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
    2620              :     {
    2621          831 :         if (((frameOptions & FRAMEOPTION_START_CURRENT_ROW) &&
    2622           38 :              node->ordNumCols != 0) ||
    2623          793 :             (frameOptions & FRAMEOPTION_START_OFFSET))
    2624          371 :             winstate->framehead_slot = ExecInitExtraTupleSlot(estate, scanDesc,
    2625              :                                                               &TTSOpsMinimalTuple);
    2626          831 :         if (((frameOptions & FRAMEOPTION_END_CURRENT_ROW) &&
    2627          439 :              node->ordNumCols != 0) ||
    2628          583 :             (frameOptions & FRAMEOPTION_END_OFFSET))
    2629          611 :             winstate->frametail_slot = ExecInitExtraTupleSlot(estate, scanDesc,
    2630              :                                                               &TTSOpsMinimalTuple);
    2631              :     }
    2632              : 
    2633              :     /*
    2634              :      * Initialize result slot, type and projection.
    2635              :      */
    2636         1431 :     ExecInitResultTupleSlotTL(&winstate->ss.ps, &TTSOpsVirtual);
    2637         1431 :     ExecAssignProjectionInfo(&winstate->ss.ps, NULL);
    2638              : 
    2639              :     /* Set up data for comparing tuples */
    2640         1431 :     if (node->partNumCols > 0)
    2641          341 :         winstate->partEqfunction =
    2642          341 :             execTuplesMatchPrepare(scanDesc,
    2643              :                                    node->partNumCols,
    2644          341 :                                    node->partColIdx,
    2645          341 :                                    node->partOperators,
    2646          341 :                                    node->partCollations,
    2647              :                                    &winstate->ss.ps);
    2648              : 
    2649         1431 :     if (node->ordNumCols > 0)
    2650         1103 :         winstate->ordEqfunction =
    2651         1103 :             execTuplesMatchPrepare(scanDesc,
    2652              :                                    node->ordNumCols,
    2653         1103 :                                    node->ordColIdx,
    2654         1103 :                                    node->ordOperators,
    2655         1103 :                                    node->ordCollations,
    2656              :                                    &winstate->ss.ps);
    2657              : 
    2658              :     /*
    2659              :      * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
    2660              :      */
    2661         1431 :     numfuncs = winstate->numfuncs;
    2662         1431 :     numaggs = winstate->numaggs;
    2663         1431 :     econtext = winstate->ss.ps.ps_ExprContext;
    2664         1431 :     econtext->ecxt_aggvalues = palloc0_array(Datum, numfuncs);
    2665         1431 :     econtext->ecxt_aggnulls = palloc0_array(bool, numfuncs);
    2666              : 
    2667              :     /*
    2668              :      * allocate per-wfunc/per-agg state information.
    2669              :      */
    2670         1431 :     perfunc = palloc0_array(WindowStatePerFuncData, numfuncs);
    2671         1431 :     peragg = palloc0_array(WindowStatePerAggData, numaggs);
    2672         1431 :     winstate->perfunc = perfunc;
    2673         1431 :     winstate->peragg = peragg;
    2674              : 
    2675         1431 :     wfuncno = -1;
    2676         1431 :     aggno = -1;
    2677         3285 :     foreach(l, winstate->funcs)
    2678              :     {
    2679         1854 :         WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
    2680         1854 :         WindowFunc *wfunc = wfuncstate->wfunc;
    2681              :         WindowStatePerFunc perfuncstate;
    2682              :         AclResult   aclresult;
    2683              :         int         i;
    2684              : 
    2685         1854 :         if (wfunc->winref != node->winref)    /* planner screwed up? */
    2686            0 :             elog(ERROR, "WindowFunc with winref %u assigned to WindowAgg with winref %u",
    2687              :                  wfunc->winref, node->winref);
    2688              : 
    2689              :         /*
    2690              :          * Look for a previous duplicate window function, which needs the same
    2691              :          * ignore_nulls value
    2692              :          */
    2693         2406 :         for (i = 0; i <= wfuncno; i++)
    2694              :         {
    2695          558 :             if (equal(wfunc, perfunc[i].wfunc) &&
    2696            6 :                 !contain_volatile_functions((Node *) wfunc))
    2697            6 :                 break;
    2698              :         }
    2699         1854 :         if (i <= wfuncno && wfunc->ignore_nulls == perfunc[i].ignore_nulls)
    2700              :         {
    2701              :             /* Found a match to an existing entry, so just mark it */
    2702            6 :             wfuncstate->wfuncno = i;
    2703            6 :             continue;
    2704              :         }
    2705              : 
    2706              :         /* Nope, so assign a new PerAgg record */
    2707         1848 :         perfuncstate = &perfunc[++wfuncno];
    2708              : 
    2709              :         /* Mark WindowFunc state node with assigned index in the result array */
    2710         1848 :         wfuncstate->wfuncno = wfuncno;
    2711              : 
    2712              :         /* Check permission to call window function */
    2713         1848 :         aclresult = object_aclcheck(ProcedureRelationId, wfunc->winfnoid, GetUserId(),
    2714              :                                     ACL_EXECUTE);
    2715         1848 :         if (aclresult != ACLCHECK_OK)
    2716            0 :             aclcheck_error(aclresult, OBJECT_FUNCTION,
    2717            0 :                            get_func_name(wfunc->winfnoid));
    2718         1848 :         InvokeFunctionExecuteHook(wfunc->winfnoid);
    2719              : 
    2720              :         /* Fill in the perfuncstate data */
    2721         1848 :         perfuncstate->wfuncstate = wfuncstate;
    2722         1848 :         perfuncstate->wfunc = wfunc;
    2723         1848 :         perfuncstate->numArguments = list_length(wfuncstate->args);
    2724         1848 :         perfuncstate->winCollation = wfunc->inputcollid;
    2725              : 
    2726         1848 :         get_typlenbyval(wfunc->wintype,
    2727              :                         &perfuncstate->resulttypeLen,
    2728              :                         &perfuncstate->resulttypeByVal);
    2729              : 
    2730              :         /*
    2731              :          * If it's really just a plain aggregate function, we'll emulate the
    2732              :          * Agg environment for it.
    2733              :          */
    2734         1848 :         perfuncstate->plain_agg = wfunc->winagg;
    2735         1848 :         if (wfunc->winagg)
    2736              :         {
    2737              :             WindowStatePerAgg peraggstate;
    2738              : 
    2739          828 :             perfuncstate->aggno = ++aggno;
    2740          828 :             peraggstate = &winstate->peragg[aggno];
    2741          828 :             initialize_peragg(winstate, wfunc, peraggstate);
    2742          828 :             peraggstate->wfuncno = wfuncno;
    2743              :         }
    2744              :         else
    2745              :         {
    2746         1020 :             WindowObject winobj = makeNode(WindowObjectData);
    2747              : 
    2748         1020 :             winobj->winstate = winstate;
    2749         1020 :             winobj->argstates = wfuncstate->args;
    2750         1020 :             winobj->localmem = NULL;
    2751         1020 :             perfuncstate->winobj = winobj;
    2752         1020 :             winobj->ignore_nulls = wfunc->ignore_nulls;
    2753         1020 :             init_notnull_info(winobj, perfuncstate);
    2754              : 
    2755              :             /* It's a real window function, so set up to call it. */
    2756         1020 :             fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo,
    2757              :                           econtext->ecxt_per_query_memory);
    2758         1020 :             fmgr_info_set_expr((Node *) wfunc, &perfuncstate->flinfo);
    2759              :         }
    2760              :     }
    2761              : 
    2762              :     /* Update numfuncs, numaggs to match number of unique functions found */
    2763         1431 :     winstate->numfuncs = wfuncno + 1;
    2764         1431 :     winstate->numaggs = aggno + 1;
    2765              : 
    2766              :     /* Set up WindowObject for aggregates, if needed */
    2767         1431 :     if (winstate->numaggs > 0)
    2768              :     {
    2769          771 :         WindowObject agg_winobj = makeNode(WindowObjectData);
    2770              : 
    2771          771 :         agg_winobj->winstate = winstate;
    2772          771 :         agg_winobj->argstates = NIL;
    2773          771 :         agg_winobj->localmem = NULL;
    2774              :         /* make sure markptr = -1 to invalidate. It may not get used */
    2775          771 :         agg_winobj->markptr = -1;
    2776          771 :         agg_winobj->readptr = -1;
    2777          771 :         winstate->agg_winobj = agg_winobj;
    2778              :     }
    2779              : 
    2780              :     /* Set the status to running */
    2781         1431 :     winstate->status = WINDOWAGG_RUN;
    2782              : 
    2783              :     /* initialize frame bound offset expressions */
    2784         1431 :     winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
    2785              :                                          (PlanState *) winstate);
    2786         1431 :     winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
    2787              :                                        (PlanState *) winstate);
    2788              : 
    2789              :     /* Lookup in_range support functions if needed */
    2790         1431 :     if (OidIsValid(node->startInRangeFunc))
    2791          261 :         fmgr_info(node->startInRangeFunc, &winstate->startInRangeFunc);
    2792         1431 :     if (OidIsValid(node->endInRangeFunc))
    2793          294 :         fmgr_info(node->endInRangeFunc, &winstate->endInRangeFunc);
    2794         1431 :     winstate->inRangeColl = node->inRangeColl;
    2795         1431 :     winstate->inRangeAsc = node->inRangeAsc;
    2796         1431 :     winstate->inRangeNullsFirst = node->inRangeNullsFirst;
    2797              : 
    2798         1431 :     winstate->all_first = true;
    2799         1431 :     winstate->partition_spooled = false;
    2800         1431 :     winstate->more_partitions = false;
    2801         1431 :     winstate->next_partition = true;
    2802              : 
    2803         1431 :     return winstate;
    2804              : }
    2805              : 
    2806              : /* -----------------
    2807              :  * ExecEndWindowAgg
    2808              :  * -----------------
    2809              :  */
    2810              : void
    2811         1329 : ExecEndWindowAgg(WindowAggState *node)
    2812              : {
    2813              :     PlanState  *outerPlan;
    2814              :     int         i;
    2815              : 
    2816         1329 :     if (node->buffer != NULL)
    2817              :     {
    2818         1050 :         tuplestore_end(node->buffer);
    2819              : 
    2820              :         /* nullify so that release_partition skips the tuplestore_clear() */
    2821         1050 :         node->buffer = NULL;
    2822              :     }
    2823              : 
    2824         1329 :     release_partition(node);
    2825              : 
    2826         2136 :     for (i = 0; i < node->numaggs; i++)
    2827              :     {
    2828          807 :         if (node->peragg[i].aggcontext != node->aggcontext)
    2829          393 :             MemoryContextDelete(node->peragg[i].aggcontext);
    2830              :     }
    2831         1329 :     MemoryContextDelete(node->partcontext);
    2832         1329 :     MemoryContextDelete(node->aggcontext);
    2833              : 
    2834         1329 :     pfree(node->perfunc);
    2835         1329 :     pfree(node->peragg);
    2836              : 
    2837         1329 :     outerPlan = outerPlanState(node);
    2838         1329 :     ExecEndNode(outerPlan);
    2839         1329 : }
    2840              : 
    2841              : /* -----------------
    2842              :  * ExecReScanWindowAgg
    2843              :  * -----------------
    2844              :  */
    2845              : void
    2846           39 : ExecReScanWindowAgg(WindowAggState *node)
    2847              : {
    2848           39 :     PlanState  *outerPlan = outerPlanState(node);
    2849           39 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
    2850              : 
    2851           39 :     node->status = WINDOWAGG_RUN;
    2852           39 :     node->all_first = true;
    2853              : 
    2854              :     /* release tuplestore et al */
    2855           39 :     release_partition(node);
    2856              : 
    2857              :     /* release all temp tuples, but especially first_part_slot */
    2858           39 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
    2859           39 :     ExecClearTuple(node->first_part_slot);
    2860           39 :     ExecClearTuple(node->agg_row_slot);
    2861           39 :     ExecClearTuple(node->temp_slot_1);
    2862           39 :     ExecClearTuple(node->temp_slot_2);
    2863           39 :     if (node->framehead_slot)
    2864            0 :         ExecClearTuple(node->framehead_slot);
    2865           39 :     if (node->frametail_slot)
    2866            3 :         ExecClearTuple(node->frametail_slot);
    2867              : 
    2868              :     /* Forget current wfunc values */
    2869           78 :     MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numfuncs);
    2870           39 :     MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numfuncs);
    2871              : 
    2872              :     /*
    2873              :      * if chgParam of subnode is not null then plan will be re-scanned by
    2874              :      * first ExecProcNode.
    2875              :      */
    2876           39 :     if (outerPlan->chgParam == NULL)
    2877            3 :         ExecReScan(outerPlan);
    2878           39 : }
    2879              : 
    2880              : /*
    2881              :  * initialize_peragg
    2882              :  *
    2883              :  * Almost same as in nodeAgg.c, except we don't support DISTINCT currently.
    2884              :  */
    2885              : static WindowStatePerAggData *
    2886          828 : initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
    2887              :                   WindowStatePerAgg peraggstate)
    2888              : {
    2889              :     Oid         inputTypes[FUNC_MAX_ARGS];
    2890              :     int         numArguments;
    2891              :     HeapTuple   aggTuple;
    2892              :     Form_pg_aggregate aggform;
    2893              :     Oid         aggtranstype;
    2894              :     AttrNumber  initvalAttNo;
    2895              :     AclResult   aclresult;
    2896              :     bool        use_ma_code;
    2897              :     Oid         transfn_oid,
    2898              :                 invtransfn_oid,
    2899              :                 finalfn_oid;
    2900              :     bool        finalextra;
    2901              :     char        finalmodify;
    2902              :     Expr       *transfnexpr,
    2903              :                *invtransfnexpr,
    2904              :                *finalfnexpr;
    2905              :     Datum       textInitVal;
    2906              :     int         i;
    2907              :     ListCell   *lc;
    2908              : 
    2909          828 :     numArguments = list_length(wfunc->args);
    2910              : 
    2911          828 :     i = 0;
    2912         1599 :     foreach(lc, wfunc->args)
    2913              :     {
    2914          771 :         inputTypes[i++] = exprType((Node *) lfirst(lc));
    2915              :     }
    2916              : 
    2917          828 :     aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(wfunc->winfnoid));
    2918          828 :     if (!HeapTupleIsValid(aggTuple))
    2919            0 :         elog(ERROR, "cache lookup failed for aggregate %u",
    2920              :              wfunc->winfnoid);
    2921          828 :     aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
    2922              : 
    2923              :     /*
    2924              :      * Figure out whether we want to use the moving-aggregate implementation,
    2925              :      * and collect the right set of fields from the pg_aggregate entry.
    2926              :      *
    2927              :      * It's possible that an aggregate would supply a safe moving-aggregate
    2928              :      * implementation and an unsafe normal one, in which case our hand is
    2929              :      * forced.  Otherwise, if the frame head can't move, we don't need
    2930              :      * moving-aggregate code.  Even if we'd like to use it, don't do so if the
    2931              :      * aggregate's arguments (and FILTER clause if any) contain any calls to
    2932              :      * volatile functions.  Otherwise, the difference between restarting and
    2933              :      * not restarting the aggregation would be user-visible.
    2934              :      *
    2935              :      * We also don't risk using moving aggregates when there are subplans in
    2936              :      * the arguments or FILTER clause.  This is partly because
    2937              :      * contain_volatile_functions() doesn't look inside subplans; but there
    2938              :      * are other reasons why a subplan's output might be volatile.  For
    2939              :      * example, syncscan mode can render the results nonrepeatable.
    2940              :      */
    2941          828 :     if (!OidIsValid(aggform->aggminvtransfn))
    2942          154 :         use_ma_code = false;    /* sine qua non */
    2943          674 :     else if (aggform->aggmfinalmodify == AGGMODIFY_READ_ONLY &&
    2944          674 :              aggform->aggfinalmodify != AGGMODIFY_READ_ONLY)
    2945            0 :         use_ma_code = true;     /* decision forced by safety */
    2946          674 :     else if (winstate->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
    2947          269 :         use_ma_code = false;    /* non-moving frame head */
    2948          405 :     else if (contain_volatile_functions((Node *) wfunc))
    2949            6 :         use_ma_code = false;    /* avoid possible behavioral change */
    2950          399 :     else if (contain_subplans((Node *) wfunc))
    2951            0 :         use_ma_code = false;    /* subplans might contain volatile functions */
    2952              :     else
    2953          399 :         use_ma_code = true;     /* yes, let's use it */
    2954          828 :     if (use_ma_code)
    2955              :     {
    2956          399 :         peraggstate->transfn_oid = transfn_oid = aggform->aggmtransfn;
    2957          399 :         peraggstate->invtransfn_oid = invtransfn_oid = aggform->aggminvtransfn;
    2958          399 :         peraggstate->finalfn_oid = finalfn_oid = aggform->aggmfinalfn;
    2959          399 :         finalextra = aggform->aggmfinalextra;
    2960          399 :         finalmodify = aggform->aggmfinalmodify;
    2961          399 :         aggtranstype = aggform->aggmtranstype;
    2962          399 :         initvalAttNo = Anum_pg_aggregate_aggminitval;
    2963              :     }
    2964              :     else
    2965              :     {
    2966          429 :         peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
    2967          429 :         peraggstate->invtransfn_oid = invtransfn_oid = InvalidOid;
    2968          429 :         peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
    2969          429 :         finalextra = aggform->aggfinalextra;
    2970          429 :         finalmodify = aggform->aggfinalmodify;
    2971          429 :         aggtranstype = aggform->aggtranstype;
    2972          429 :         initvalAttNo = Anum_pg_aggregate_agginitval;
    2973              :     }
    2974              : 
    2975              :     /*
    2976              :      * ExecInitWindowAgg already checked permission to call aggregate function
    2977              :      * ... but we still need to check the component functions
    2978              :      */
    2979              : 
    2980              :     /* Check that aggregate owner has permission to call component fns */
    2981              :     {
    2982              :         HeapTuple   procTuple;
    2983              :         Oid         aggOwner;
    2984              : 
    2985          828 :         procTuple = SearchSysCache1(PROCOID,
    2986              :                                     ObjectIdGetDatum(wfunc->winfnoid));
    2987          828 :         if (!HeapTupleIsValid(procTuple))
    2988            0 :             elog(ERROR, "cache lookup failed for function %u",
    2989              :                  wfunc->winfnoid);
    2990          828 :         aggOwner = ((Form_pg_proc) GETSTRUCT(procTuple))->proowner;
    2991          828 :         ReleaseSysCache(procTuple);
    2992              : 
    2993          828 :         aclresult = object_aclcheck(ProcedureRelationId, transfn_oid, aggOwner,
    2994              :                                     ACL_EXECUTE);
    2995          828 :         if (aclresult != ACLCHECK_OK)
    2996            0 :             aclcheck_error(aclresult, OBJECT_FUNCTION,
    2997            0 :                            get_func_name(transfn_oid));
    2998          828 :         InvokeFunctionExecuteHook(transfn_oid);
    2999              : 
    3000          828 :         if (OidIsValid(invtransfn_oid))
    3001              :         {
    3002          399 :             aclresult = object_aclcheck(ProcedureRelationId, invtransfn_oid, aggOwner,
    3003              :                                         ACL_EXECUTE);
    3004          399 :             if (aclresult != ACLCHECK_OK)
    3005            0 :                 aclcheck_error(aclresult, OBJECT_FUNCTION,
    3006            0 :                                get_func_name(invtransfn_oid));
    3007          399 :             InvokeFunctionExecuteHook(invtransfn_oid);
    3008              :         }
    3009              : 
    3010          828 :         if (OidIsValid(finalfn_oid))
    3011              :         {
    3012          427 :             aclresult = object_aclcheck(ProcedureRelationId, finalfn_oid, aggOwner,
    3013              :                                         ACL_EXECUTE);
    3014          427 :             if (aclresult != ACLCHECK_OK)
    3015            0 :                 aclcheck_error(aclresult, OBJECT_FUNCTION,
    3016            0 :                                get_func_name(finalfn_oid));
    3017          427 :             InvokeFunctionExecuteHook(finalfn_oid);
    3018              :         }
    3019              :     }
    3020              : 
    3021              :     /*
    3022              :      * If the selected finalfn isn't read-only, we can't run this aggregate as
    3023              :      * a window function.  This is a user-facing error, so we take a bit more
    3024              :      * care with the error message than elsewhere in this function.
    3025              :      */
    3026          828 :     if (finalmodify != AGGMODIFY_READ_ONLY)
    3027            0 :         ereport(ERROR,
    3028              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3029              :                  errmsg("aggregate function %s does not support use as a window function",
    3030              :                         format_procedure(wfunc->winfnoid))));
    3031              : 
    3032              :     /* Detect how many arguments to pass to the finalfn */
    3033          828 :     if (finalextra)
    3034           13 :         peraggstate->numFinalArgs = numArguments + 1;
    3035              :     else
    3036          815 :         peraggstate->numFinalArgs = 1;
    3037              : 
    3038              :     /* resolve actual type of transition state, if polymorphic */
    3039          828 :     aggtranstype = resolve_aggregate_transtype(wfunc->winfnoid,
    3040              :                                                aggtranstype,
    3041              :                                                inputTypes,
    3042              :                                                numArguments);
    3043              : 
    3044              :     /* build expression trees using actual argument & result types */
    3045          828 :     build_aggregate_transfn_expr(inputTypes,
    3046              :                                  numArguments,
    3047              :                                  0, /* no ordered-set window functions yet */
    3048              :                                  false, /* no variadic window functions yet */
    3049              :                                  aggtranstype,
    3050              :                                  wfunc->inputcollid,
    3051              :                                  transfn_oid,
    3052              :                                  invtransfn_oid,
    3053              :                                  &transfnexpr,
    3054              :                                  &invtransfnexpr);
    3055              : 
    3056              :     /* set up infrastructure for calling the transfn(s) and finalfn */
    3057          828 :     fmgr_info(transfn_oid, &peraggstate->transfn);
    3058          828 :     fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
    3059              : 
    3060          828 :     if (OidIsValid(invtransfn_oid))
    3061              :     {
    3062          399 :         fmgr_info(invtransfn_oid, &peraggstate->invtransfn);
    3063          399 :         fmgr_info_set_expr((Node *) invtransfnexpr, &peraggstate->invtransfn);
    3064              :     }
    3065              : 
    3066          828 :     if (OidIsValid(finalfn_oid))
    3067              :     {
    3068          427 :         build_aggregate_finalfn_expr(inputTypes,
    3069              :                                      peraggstate->numFinalArgs,
    3070              :                                      aggtranstype,
    3071              :                                      wfunc->wintype,
    3072              :                                      wfunc->inputcollid,
    3073              :                                      finalfn_oid,
    3074              :                                      &finalfnexpr);
    3075          427 :         fmgr_info(finalfn_oid, &peraggstate->finalfn);
    3076          427 :         fmgr_info_set_expr((Node *) finalfnexpr, &peraggstate->finalfn);
    3077              :     }
    3078              : 
    3079              :     /* get info about relevant datatypes */
    3080          828 :     get_typlenbyval(wfunc->wintype,
    3081              :                     &peraggstate->resulttypeLen,
    3082              :                     &peraggstate->resulttypeByVal);
    3083          828 :     get_typlenbyval(aggtranstype,
    3084              :                     &peraggstate->transtypeLen,
    3085              :                     &peraggstate->transtypeByVal);
    3086              : 
    3087              :     /*
    3088              :      * initval is potentially null, so don't try to access it as a struct
    3089              :      * field. Must do it the hard way with SysCacheGetAttr.
    3090              :      */
    3091          828 :     textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, initvalAttNo,
    3092              :                                   &peraggstate->initValueIsNull);
    3093              : 
    3094          828 :     if (peraggstate->initValueIsNull)
    3095          475 :         peraggstate->initValue = (Datum) 0;
    3096              :     else
    3097          353 :         peraggstate->initValue = GetAggInitVal(textInitVal,
    3098              :                                                aggtranstype);
    3099              : 
    3100              :     /*
    3101              :      * If the transfn is strict and the initval is NULL, make sure input type
    3102              :      * and transtype are the same (or at least binary-compatible), so that
    3103              :      * it's OK to use the first input value as the initial transValue.  This
    3104              :      * should have been checked at agg definition time, but we must check
    3105              :      * again in case the transfn's strictness property has been changed.
    3106              :      */
    3107          828 :     if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull)
    3108              :     {
    3109          132 :         if (numArguments < 1 ||
    3110          132 :             !IsBinaryCoercible(inputTypes[0], aggtranstype))
    3111            0 :             ereport(ERROR,
    3112              :                     (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
    3113              :                      errmsg("aggregate %u needs to have compatible input type and transition type",
    3114              :                             wfunc->winfnoid)));
    3115              :     }
    3116              : 
    3117              :     /*
    3118              :      * Insist that forward and inverse transition functions have the same
    3119              :      * strictness setting.  Allowing them to differ would require handling
    3120              :      * more special cases in advance_windowaggregate and
    3121              :      * advance_windowaggregate_base, for no discernible benefit.  This should
    3122              :      * have been checked at agg definition time, but we must check again in
    3123              :      * case either function's strictness property has been changed.
    3124              :      */
    3125          828 :     if (OidIsValid(invtransfn_oid) &&
    3126          399 :         peraggstate->transfn.fn_strict != peraggstate->invtransfn.fn_strict)
    3127            0 :         ereport(ERROR,
    3128              :                 (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
    3129              :                  errmsg("strictness of aggregate's forward and inverse transition functions must match")));
    3130              : 
    3131              :     /*
    3132              :      * Moving aggregates use their own aggcontext.
    3133              :      *
    3134              :      * This is necessary because they might restart at different times, so we
    3135              :      * might never be able to reset the shared context otherwise.  We can't
    3136              :      * make it the aggregates' responsibility to clean up after themselves,
    3137              :      * because strict aggregates must be restarted whenever we remove their
    3138              :      * last non-NULL input, which the aggregate won't be aware is happening.
    3139              :      * Also, just pfree()ing the transValue upon restarting wouldn't help,
    3140              :      * since we'd miss any indirectly referenced data.  We could, in theory,
    3141              :      * make the memory allocation rules for moving aggregates different than
    3142              :      * they have historically been for plain aggregates, but that seems grotty
    3143              :      * and likely to lead to memory leaks.
    3144              :      */
    3145          828 :     if (OidIsValid(invtransfn_oid))
    3146          399 :         peraggstate->aggcontext =
    3147          399 :             AllocSetContextCreate(CurrentMemoryContext,
    3148              :                                   "WindowAgg Per Aggregate",
    3149              :                                   ALLOCSET_DEFAULT_SIZES);
    3150              :     else
    3151          429 :         peraggstate->aggcontext = winstate->aggcontext;
    3152              : 
    3153          828 :     ReleaseSysCache(aggTuple);
    3154              : 
    3155          828 :     return peraggstate;
    3156              : }
    3157              : 
    3158              : static Datum
    3159          353 : GetAggInitVal(Datum textInitVal, Oid transtype)
    3160              : {
    3161              :     Oid         typinput,
    3162              :                 typioparam;
    3163              :     char       *strInitVal;
    3164              :     Datum       initVal;
    3165              : 
    3166          353 :     getTypeInputInfo(transtype, &typinput, &typioparam);
    3167          353 :     strInitVal = TextDatumGetCString(textInitVal);
    3168          353 :     initVal = OidInputFunctionCall(typinput, strInitVal,
    3169              :                                    typioparam, -1);
    3170          353 :     pfree(strInitVal);
    3171          353 :     return initVal;
    3172              : }
    3173              : 
    3174              : /*
    3175              :  * are_peers
    3176              :  * compare two rows to see if they are equal according to the ORDER BY clause
    3177              :  *
    3178              :  * NB: this does not consider the window frame mode.
    3179              :  */
    3180              : static bool
    3181       298716 : are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
    3182              :           TupleTableSlot *slot2)
    3183              : {
    3184       298716 :     WindowAgg  *node = (WindowAgg *) winstate->ss.ps.plan;
    3185       298716 :     ExprContext *econtext = winstate->tmpcontext;
    3186              : 
    3187              :     /* If no ORDER BY, all rows are peers with each other */
    3188       298716 :     if (node->ordNumCols == 0)
    3189        15530 :         return true;
    3190              : 
    3191       283186 :     econtext->ecxt_outertuple = slot1;
    3192       283186 :     econtext->ecxt_innertuple = slot2;
    3193       283186 :     return ExecQualAndReset(winstate->ordEqfunction, econtext);
    3194              : }
    3195              : 
    3196              : /*
    3197              :  * window_gettupleslot
    3198              :  *  Fetch the pos'th tuple of the current partition into the slot,
    3199              :  *  using the winobj's read pointer
    3200              :  *
    3201              :  * Returns true if successful, false if no such row
    3202              :  */
    3203              : static bool
    3204       381740 : window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
    3205              : {
    3206       381740 :     WindowAggState *winstate = winobj->winstate;
    3207              :     MemoryContext oldcontext;
    3208              : 
    3209              :     /* often called repeatedly in a row */
    3210       381740 :     CHECK_FOR_INTERRUPTS();
    3211              : 
    3212              :     /* Don't allow passing -1 to spool_tuples here */
    3213       381740 :     if (pos < 0)
    3214          204 :         return false;
    3215              : 
    3216              :     /* If necessary, fetch the tuple into the spool */
    3217       381536 :     spool_tuples(winstate, pos);
    3218              : 
    3219       381536 :     if (pos >= winstate->spooled_rows)
    3220         2383 :         return false;
    3221              : 
    3222       379153 :     if (pos < winobj->markpos)
    3223            0 :         elog(ERROR, "cannot fetch row before WindowObject's mark position");
    3224              : 
    3225       379153 :     oldcontext = MemoryContextSwitchTo(winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    3226              : 
    3227       379153 :     tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
    3228              : 
    3229              :     /*
    3230              :      * Advance or rewind until we are within one tuple of the one we want.
    3231              :      */
    3232       379153 :     if (winobj->seekpos < pos - 1)
    3233              :     {
    3234         1245 :         if (!tuplestore_skiptuples(winstate->buffer,
    3235         1245 :                                    pos - 1 - winobj->seekpos,
    3236              :                                    true))
    3237            0 :             elog(ERROR, "unexpected end of tuplestore");
    3238         1245 :         winobj->seekpos = pos - 1;
    3239              :     }
    3240       377908 :     else if (winobj->seekpos > pos + 1)
    3241              :     {
    3242         1382 :         if (!tuplestore_skiptuples(winstate->buffer,
    3243         1382 :                                    winobj->seekpos - (pos + 1),
    3244              :                                    false))
    3245            0 :             elog(ERROR, "unexpected end of tuplestore");
    3246         1382 :         winobj->seekpos = pos + 1;
    3247              :     }
    3248       376526 :     else if (winobj->seekpos == pos)
    3249              :     {
    3250              :         /*
    3251              :          * There's no API to refetch the tuple at the current position.  We
    3252              :          * have to move one tuple forward, and then one backward.  (We don't
    3253              :          * do it the other way because we might try to fetch the row before
    3254              :          * our mark, which isn't allowed.)  XXX this case could stand to be
    3255              :          * optimized.
    3256              :          */
    3257        86561 :         tuplestore_advance(winstate->buffer, true);
    3258        86561 :         winobj->seekpos++;
    3259              :     }
    3260              : 
    3261              :     /*
    3262              :      * Now we should be on the tuple immediately before or after the one we
    3263              :      * want, so just fetch forwards or backwards as appropriate.
    3264              :      *
    3265              :      * Notice that we tell tuplestore_gettupleslot to make a physical copy of
    3266              :      * the fetched tuple.  This ensures that the slot's contents remain valid
    3267              :      * through manipulations of the tuplestore, which some callers depend on.
    3268              :      */
    3269       379153 :     if (winobj->seekpos > pos)
    3270              :     {
    3271        88080 :         if (!tuplestore_gettupleslot(winstate->buffer, false, true, slot))
    3272            0 :             elog(ERROR, "unexpected end of tuplestore");
    3273        88080 :         winobj->seekpos--;
    3274              :     }
    3275              :     else
    3276              :     {
    3277       291073 :         if (!tuplestore_gettupleslot(winstate->buffer, true, true, slot))
    3278            0 :             elog(ERROR, "unexpected end of tuplestore");
    3279       291073 :         winobj->seekpos++;
    3280              :     }
    3281              : 
    3282              :     Assert(winobj->seekpos == pos);
    3283              : 
    3284       379153 :     MemoryContextSwitchTo(oldcontext);
    3285              : 
    3286       379153 :     return true;
    3287              : }
    3288              : 
    3289              : /* gettuple_eval_partition
    3290              :  * get tuple in a partition and evaluate the window function's argument
    3291              :  * expression on it.
    3292              :  */
    3293              : static Datum
    3294       118770 : gettuple_eval_partition(WindowObject winobj, int argno,
    3295              :                         int64 abs_pos, bool *isnull, bool *isout)
    3296              : {
    3297              :     WindowAggState *winstate;
    3298              :     ExprContext *econtext;
    3299              :     TupleTableSlot *slot;
    3300              : 
    3301       118770 :     winstate = winobj->winstate;
    3302       118770 :     slot = winstate->temp_slot_1;
    3303       118770 :     if (!window_gettupleslot(winobj, abs_pos, slot))
    3304              :     {
    3305              :         /* out of partition */
    3306          291 :         if (isout)
    3307          291 :             *isout = true;
    3308          291 :         *isnull = true;
    3309          291 :         return (Datum) 0;
    3310              :     }
    3311              : 
    3312       118479 :     if (isout)
    3313       118479 :         *isout = false;
    3314       118479 :     econtext = winstate->ss.ps.ps_ExprContext;
    3315       118479 :     econtext->ecxt_outertuple = slot;
    3316       118479 :     return ExecEvalExpr((ExprState *) list_nth
    3317       118479 :                         (winobj->argstates, argno),
    3318              :                         econtext, isnull);
    3319              : }
    3320              : 
    3321              : /*
    3322              :  * ignorenulls_getfuncarginframe
    3323              :  * For IGNORE NULLS, get the next nonnull value in the frame, moving forward
    3324              :  * or backward until we find a value or reach the frame's end.
    3325              :  */
    3326              : static Datum
    3327          480 : ignorenulls_getfuncarginframe(WindowObject winobj, int argno,
    3328              :                               int relpos, int seektype, bool set_mark,
    3329              :                               bool *isnull, bool *isout)
    3330              : {
    3331              :     WindowAggState *winstate;
    3332              :     ExprContext *econtext;
    3333              :     TupleTableSlot *slot;
    3334              :     Datum       datum;
    3335              :     int64       abs_pos;
    3336              :     int64       mark_pos;
    3337              :     int         notnull_offset;
    3338              :     int         notnull_relpos;
    3339              :     int         forward;
    3340              : 
    3341              :     Assert(WindowObjectIsValid(winobj));
    3342          480 :     winstate = winobj->winstate;
    3343          480 :     econtext = winstate->ss.ps.ps_ExprContext;
    3344          480 :     slot = winstate->temp_slot_1;
    3345          480 :     datum = (Datum) 0;
    3346          480 :     notnull_offset = 0;
    3347          480 :     notnull_relpos = abs(relpos);
    3348              : 
    3349          480 :     switch (seektype)
    3350              :     {
    3351            0 :         case WINDOW_SEEK_CURRENT:
    3352            0 :             elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
    3353              :             abs_pos = mark_pos = 0; /* keep compiler quiet */
    3354              :             break;
    3355          330 :         case WINDOW_SEEK_HEAD:
    3356              :             /* rejecting relpos < 0 is easy and simplifies code below */
    3357          330 :             if (relpos < 0)
    3358            0 :                 goto out_of_frame;
    3359          330 :             update_frameheadpos(winstate);
    3360          330 :             abs_pos = winstate->frameheadpos;
    3361          330 :             mark_pos = winstate->frameheadpos;
    3362          330 :             forward = 1;
    3363          330 :             break;
    3364          150 :         case WINDOW_SEEK_TAIL:
    3365              :             /* rejecting relpos > 0 is easy and simplifies code below */
    3366          150 :             if (relpos > 0)
    3367            0 :                 goto out_of_frame;
    3368          150 :             update_frametailpos(winstate);
    3369          150 :             abs_pos = winstate->frametailpos - 1;
    3370          150 :             mark_pos = 0;       /* keep compiler quiet */
    3371          150 :             forward = -1;
    3372          150 :             break;
    3373            0 :         default:
    3374            0 :             elog(ERROR, "unrecognized window seek type: %d", seektype);
    3375              :             abs_pos = mark_pos = 0; /* keep compiler quiet */
    3376              :             break;
    3377              :     }
    3378              : 
    3379              :     /*
    3380              :      * Get the next nonnull value in the frame, moving forward or backward
    3381              :      * until we find a value or reach the frame's end.
    3382              :      */
    3383              :     do
    3384              :     {
    3385              :         int         inframe;
    3386              :         int         v;
    3387              : 
    3388              :         /*
    3389              :          * Check apparent out of frame case.  We need to do this because we
    3390              :          * may not call window_gettupleslot before row_is_in_frame, which
    3391              :          * supposes abs_pos is never negative.
    3392              :          */
    3393         1146 :         if (abs_pos < 0)
    3394            6 :             goto out_of_frame;
    3395              : 
    3396              :         /* check whether row is in frame */
    3397         1140 :         inframe = row_is_in_frame(winobj, abs_pos, slot, true);
    3398         1140 :         if (inframe == -1)
    3399           27 :             goto out_of_frame;
    3400         1113 :         else if (inframe == 0)
    3401           39 :             goto advance;
    3402              : 
    3403         1074 :         if (isout)
    3404            0 :             *isout = false;
    3405              : 
    3406         1074 :         v = get_notnull_info(winobj, abs_pos, argno);
    3407         1074 :         if (v == NN_NULL)       /* this row is known to be NULL */
    3408          300 :             goto advance;
    3409              : 
    3410          774 :         else if (v == NN_UNKNOWN)   /* need to check NULL or not */
    3411              :         {
    3412          393 :             if (!window_gettupleslot(winobj, abs_pos, slot))
    3413           15 :                 goto out_of_frame;
    3414              : 
    3415          378 :             econtext->ecxt_outertuple = slot;
    3416          378 :             datum = ExecEvalExpr(
    3417          378 :                                  (ExprState *) list_nth(winobj->argstates,
    3418              :                                                         argno), econtext,
    3419              :                                  isnull);
    3420          378 :             if (!*isnull)
    3421          225 :                 notnull_offset++;
    3422              : 
    3423              :             /* record the row status */
    3424          378 :             put_notnull_info(winobj, abs_pos, argno, *isnull);
    3425              :         }
    3426              :         else                    /* this row is known to be NOT NULL */
    3427              :         {
    3428          381 :             notnull_offset++;
    3429          381 :             if (notnull_offset > notnull_relpos)
    3430              :             {
    3431              :                 /* to prepare exiting this loop, datum needs to be set */
    3432          240 :                 if (!window_gettupleslot(winobj, abs_pos, slot))
    3433            0 :                     goto out_of_frame;
    3434              : 
    3435          240 :                 econtext->ecxt_outertuple = slot;
    3436          240 :                 datum = ExecEvalExpr(
    3437          240 :                                      (ExprState *) list_nth
    3438          240 :                                      (winobj->argstates, argno),
    3439              :                                      econtext, isnull);
    3440              :             }
    3441              :         }
    3442          141 : advance:
    3443         1098 :         abs_pos += forward;
    3444         1098 :     } while (notnull_offset <= notnull_relpos);
    3445              : 
    3446          432 :     if (set_mark)
    3447          432 :         WinSetMarkPosition(winobj, mark_pos);
    3448              : 
    3449          432 :     return datum;
    3450              : 
    3451           48 : out_of_frame:
    3452           48 :     if (isout)
    3453            0 :         *isout = true;
    3454           48 :     *isnull = true;
    3455           48 :     return (Datum) 0;
    3456              : }
    3457              : 
    3458              : 
    3459              : /*
    3460              :  * init_notnull_info
    3461              :  * Initialize non null map.
    3462              :  */
    3463              : static void
    3464         1020 : init_notnull_info(WindowObject winobj, WindowStatePerFunc perfuncstate)
    3465              : {
    3466         1020 :     int         numargs = perfuncstate->numArguments;
    3467              : 
    3468         1020 :     if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
    3469              :     {
    3470           93 :         winobj->notnull_info = palloc0_array(uint8 *, numargs);
    3471           93 :         winobj->num_notnull_info = palloc0_array(int64, numargs);
    3472              :     }
    3473         1020 : }
    3474              : 
    3475              : /*
    3476              :  * grow_notnull_info
    3477              :  * expand notnull_info if necessary.
    3478              :  * pos: not null info position
    3479              :  * argno: argument number
    3480              : */
    3481              : static void
    3482         1974 : grow_notnull_info(WindowObject winobj, int64 pos, int argno)
    3483              : {
    3484              : /* initial number of notnull info members */
    3485              : #define INIT_NOT_NULL_INFO_NUM  128
    3486              : 
    3487         1974 :     if (pos >= winobj->num_notnull_info[argno])
    3488              :     {
    3489              :         /* We may be called in a short-lived context */
    3490           75 :         MemoryContext oldcontext = MemoryContextSwitchTo
    3491           75 :             (winobj->winstate->ss.ps.ps_ExprContext->ecxt_per_query_memory);
    3492              : 
    3493              :         for (;;)
    3494            0 :         {
    3495           75 :             Size        oldsize = NN_POS_TO_BYTES
    3496              :                 (winobj->num_notnull_info[argno]);
    3497              :             Size        newsize;
    3498              : 
    3499           75 :             if (oldsize == 0)   /* memory has not been allocated yet for this
    3500              :                                  * arg */
    3501              :             {
    3502           75 :                 newsize = NN_POS_TO_BYTES(INIT_NOT_NULL_INFO_NUM);
    3503           75 :                 winobj->notnull_info[argno] = palloc0(newsize);
    3504              :             }
    3505              :             else
    3506              :             {
    3507            0 :                 newsize = oldsize * 2;
    3508            0 :                 winobj->notnull_info[argno] =
    3509            0 :                     repalloc0(winobj->notnull_info[argno], oldsize, newsize);
    3510              :             }
    3511           75 :             winobj->num_notnull_info[argno] = NN_BYTES_TO_POS(newsize);
    3512           75 :             if (winobj->num_notnull_info[argno] > pos)
    3513           75 :                 break;
    3514              :         }
    3515           75 :         MemoryContextSwitchTo(oldcontext);
    3516              :     }
    3517         1974 : }
    3518              : 
    3519              : /*
    3520              :  * get_notnull_info
    3521              :  * retrieve a map
    3522              :  * pos: map position
    3523              :  * argno: argument number
    3524              :  */
    3525              : static uint8
    3526         1386 : get_notnull_info(WindowObject winobj, int64 pos, int argno)
    3527              : {
    3528              :     uint8      *mbp;
    3529              :     uint8       mb;
    3530              :     int64       bpos;
    3531              : 
    3532         1386 :     grow_notnull_info(winobj, pos, argno);
    3533         1386 :     bpos = NN_POS_TO_BYTES(pos);
    3534         1386 :     mbp = winobj->notnull_info[argno];
    3535         1386 :     mb = mbp[bpos];
    3536         1386 :     return (mb >> (NN_SHIFT(pos))) & NN_MASK;
    3537              : }
    3538              : 
    3539              : /*
    3540              :  * put_notnull_info
    3541              :  * update map
    3542              :  * pos: map position
    3543              :  * argno: argument number
    3544              :  * isnull: indicate NULL or NOT
    3545              :  */
    3546              : static void
    3547          588 : put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull)
    3548              : {
    3549              :     uint8      *mbp;
    3550              :     uint8       mb;
    3551              :     int64       bpos;
    3552          588 :     uint8       val = isnull ? NN_NULL : NN_NOTNULL;
    3553              :     int         shift;
    3554              : 
    3555          588 :     grow_notnull_info(winobj, pos, argno);
    3556          588 :     bpos = NN_POS_TO_BYTES(pos);
    3557          588 :     mbp = winobj->notnull_info[argno];
    3558          588 :     mb = mbp[bpos];
    3559          588 :     shift = NN_SHIFT(pos);
    3560          588 :     mb &= ~(NN_MASK << shift);    /* clear map */
    3561          588 :     mb |= (val << shift);     /* update map */
    3562          588 :     mbp[bpos] = mb;
    3563          588 : }
    3564              : 
    3565              : /***********************************************************************
    3566              :  * API exposed to window functions
    3567              :  ***********************************************************************/
    3568              : 
    3569              : 
    3570              : /*
    3571              :  * WinCheckAndInitializeNullTreatment
    3572              :  *      Check null treatment clause and sets ignore_nulls
    3573              :  *
    3574              :  * Window functions should call this to check if they are being called with
    3575              :  * a null treatment clause when they don't allow it, or to set ignore_nulls.
    3576              :  */
    3577              : void
    3578       435464 : WinCheckAndInitializeNullTreatment(WindowObject winobj,
    3579              :                                    bool allowNullTreatment,
    3580              :                                    FunctionCallInfo fcinfo)
    3581              : {
    3582              :     Assert(WindowObjectIsValid(winobj));
    3583       435464 :     if (winobj->ignore_nulls != NO_NULLTREATMENT && !allowNullTreatment)
    3584              :     {
    3585           36 :         const char *funcname = get_func_name(fcinfo->flinfo->fn_oid);
    3586              : 
    3587           36 :         if (!funcname)
    3588            0 :             elog(ERROR, "could not get function name");
    3589           36 :         ereport(ERROR,
    3590              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3591              :                  errmsg("function %s does not allow RESPECT/IGNORE NULLS",
    3592              :                         funcname)));
    3593              :     }
    3594       435428 :     else if (winobj->ignore_nulls == PARSER_IGNORE_NULLS)
    3595           75 :         winobj->ignore_nulls = IGNORE_NULLS;
    3596       435428 : }
    3597              : 
    3598              : /*
    3599              :  * WinGetPartitionLocalMemory
    3600              :  *      Get working memory that lives till end of partition processing
    3601              :  *
    3602              :  * On first call within a given partition, this allocates and zeroes the
    3603              :  * requested amount of space.  Subsequent calls just return the same chunk.
    3604              :  *
    3605              :  * Memory obtained this way is normally used to hold state that should be
    3606              :  * automatically reset for each new partition.  If a window function wants
    3607              :  * to hold state across the whole query, fcinfo->fn_extra can be used in the
    3608              :  * usual way for that.
    3609              :  */
    3610              : void *
    3611       165965 : WinGetPartitionLocalMemory(WindowObject winobj, Size sz)
    3612              : {
    3613              :     Assert(WindowObjectIsValid(winobj));
    3614       165965 :     if (winobj->localmem == NULL)
    3615          223 :         winobj->localmem =
    3616          223 :             MemoryContextAllocZero(winobj->winstate->partcontext, sz);
    3617       165965 :     return winobj->localmem;
    3618              : }
    3619              : 
    3620              : /*
    3621              :  * WinGetCurrentPosition
    3622              :  *      Return the current row's position (counting from 0) within the current
    3623              :  *      partition.
    3624              :  */
    3625              : int64
    3626       376163 : WinGetCurrentPosition(WindowObject winobj)
    3627              : {
    3628              :     Assert(WindowObjectIsValid(winobj));
    3629       376163 :     return winobj->winstate->currentpos;
    3630              : }
    3631              : 
    3632              : /*
    3633              :  * WinGetPartitionRowCount
    3634              :  *      Return total number of rows contained in the current partition.
    3635              :  *
    3636              :  * Note: this is a relatively expensive operation because it forces the
    3637              :  * whole partition to be "spooled" into the tuplestore at once.  Once
    3638              :  * executed, however, additional calls within the same partition are cheap.
    3639              :  */
    3640              : int64
    3641          156 : WinGetPartitionRowCount(WindowObject winobj)
    3642              : {
    3643              :     Assert(WindowObjectIsValid(winobj));
    3644          156 :     spool_tuples(winobj->winstate, -1);
    3645          156 :     return winobj->winstate->spooled_rows;
    3646              : }
    3647              : 
    3648              : /*
    3649              :  * WinSetMarkPosition
    3650              :  *      Set the "mark" position for the window object, which is the oldest row
    3651              :  *      number (counting from 0) it is allowed to fetch during all subsequent
    3652              :  *      operations within the current partition.
    3653              :  *
    3654              :  * Window functions do not have to call this, but are encouraged to move the
    3655              :  * mark forward when possible to keep the tuplestore size down and prevent
    3656              :  * having to spill rows to disk.
    3657              :  */
    3658              : void
    3659       437625 : WinSetMarkPosition(WindowObject winobj, int64 markpos)
    3660              : {
    3661              :     WindowAggState *winstate;
    3662              : 
    3663              :     Assert(WindowObjectIsValid(winobj));
    3664       437625 :     winstate = winobj->winstate;
    3665              : 
    3666       437625 :     if (markpos < winobj->markpos)
    3667            0 :         elog(ERROR, "cannot move WindowObject's mark position backward");
    3668       437625 :     tuplestore_select_read_pointer(winstate->buffer, winobj->markptr);
    3669       437625 :     if (markpos > winobj->markpos)
    3670              :     {
    3671       434466 :         tuplestore_skiptuples(winstate->buffer,
    3672       434466 :                               markpos - winobj->markpos,
    3673              :                               true);
    3674       434466 :         winobj->markpos = markpos;
    3675              :     }
    3676       437625 :     tuplestore_select_read_pointer(winstate->buffer, winobj->readptr);
    3677       437625 :     if (markpos > winobj->seekpos)
    3678              :     {
    3679       231232 :         tuplestore_skiptuples(winstate->buffer,
    3680       231232 :                               markpos - winobj->seekpos,
    3681              :                               true);
    3682       231232 :         winobj->seekpos = markpos;
    3683              :     }
    3684       437625 : }
    3685              : 
    3686              : /*
    3687              :  * WinRowsArePeers
    3688              :  *      Compare two rows (specified by absolute position in partition) to see
    3689              :  *      if they are equal according to the ORDER BY clause.
    3690              :  *
    3691              :  * NB: this does not consider the window frame mode.
    3692              :  */
    3693              : bool
    3694        82782 : WinRowsArePeers(WindowObject winobj, int64 pos1, int64 pos2)
    3695              : {
    3696              :     WindowAggState *winstate;
    3697              :     WindowAgg  *node;
    3698              :     TupleTableSlot *slot1;
    3699              :     TupleTableSlot *slot2;
    3700              :     bool        res;
    3701              : 
    3702              :     Assert(WindowObjectIsValid(winobj));
    3703        82782 :     winstate = winobj->winstate;
    3704        82782 :     node = (WindowAgg *) winstate->ss.ps.plan;
    3705              : 
    3706              :     /* If no ORDER BY, all rows are peers; don't bother to fetch them */
    3707        82782 :     if (node->ordNumCols == 0)
    3708          135 :         return true;
    3709              : 
    3710              :     /*
    3711              :      * Note: OK to use temp_slot_2 here because we aren't calling any
    3712              :      * frame-related functions (those tend to clobber temp_slot_2).
    3713              :      */
    3714        82647 :     slot1 = winstate->temp_slot_1;
    3715        82647 :     slot2 = winstate->temp_slot_2;
    3716              : 
    3717        82647 :     if (!window_gettupleslot(winobj, pos1, slot1))
    3718            0 :         elog(ERROR, "specified position is out of window: " INT64_FORMAT,
    3719              :              pos1);
    3720        82647 :     if (!window_gettupleslot(winobj, pos2, slot2))
    3721            0 :         elog(ERROR, "specified position is out of window: " INT64_FORMAT,
    3722              :              pos2);
    3723              : 
    3724        82647 :     res = are_peers(winstate, slot1, slot2);
    3725              : 
    3726        82647 :     ExecClearTuple(slot1);
    3727        82647 :     ExecClearTuple(slot2);
    3728              : 
    3729        82647 :     return res;
    3730              : }
    3731              : 
    3732              : /*
    3733              :  * WinGetFuncArgInPartition
    3734              :  *      Evaluate a window function's argument expression on a specified
    3735              :  *      row of the partition.  The row is identified in lseek(2) style,
    3736              :  *      i.e. relative to the current, first, or last row.
    3737              :  *
    3738              :  * argno: argument number to evaluate (counted from 0)
    3739              :  * relpos: signed rowcount offset from the seek position
    3740              :  * seektype: WINDOW_SEEK_CURRENT, WINDOW_SEEK_HEAD, or WINDOW_SEEK_TAIL
    3741              :  * set_mark: If the row is found and set_mark is true, the mark is moved to
    3742              :  *      the row as a side-effect.
    3743              :  * isnull: output argument, receives isnull status of result
    3744              :  * isout: output argument, set to indicate whether target row position
    3745              :  *      is out of partition (can pass NULL if caller doesn't care about this)
    3746              :  *
    3747              :  * Specifying a nonexistent row is not an error, it just causes a null result
    3748              :  * (plus setting *isout true, if isout isn't NULL).
    3749              :  */
    3750              : Datum
    3751       118530 : WinGetFuncArgInPartition(WindowObject winobj, int argno,
    3752              :                          int relpos, int seektype, bool set_mark,
    3753              :                          bool *isnull, bool *isout)
    3754              : {
    3755              :     WindowAggState *winstate;
    3756              :     int64       abs_pos;
    3757              :     int64       mark_pos;
    3758              :     Datum       datum;
    3759              :     bool        null_treatment;
    3760              :     int         notnull_offset;
    3761              :     int         notnull_relpos;
    3762              :     int         forward;
    3763              :     bool        myisout;
    3764              : 
    3765              :     Assert(WindowObjectIsValid(winobj));
    3766       118530 :     winstate = winobj->winstate;
    3767              : 
    3768       118530 :     null_treatment = (winobj->ignore_nulls == IGNORE_NULLS && relpos != 0);
    3769              : 
    3770       118530 :     switch (seektype)
    3771              :     {
    3772       118530 :         case WINDOW_SEEK_CURRENT:
    3773       118530 :             if (null_treatment)
    3774          240 :                 abs_pos = winstate->currentpos;
    3775              :             else
    3776       118290 :                 abs_pos = winstate->currentpos + relpos;
    3777       118530 :             break;
    3778            0 :         case WINDOW_SEEK_HEAD:
    3779            0 :             if (null_treatment)
    3780            0 :                 abs_pos = 0;
    3781              :             else
    3782            0 :                 abs_pos = relpos;
    3783            0 :             break;
    3784            0 :         case WINDOW_SEEK_TAIL:
    3785            0 :             spool_tuples(winstate, -1);
    3786            0 :             abs_pos = winstate->spooled_rows - 1 + relpos;
    3787            0 :             break;
    3788            0 :         default:
    3789            0 :             elog(ERROR, "unrecognized window seek type: %d", seektype);
    3790              :             abs_pos = 0;        /* keep compiler quiet */
    3791              :             break;
    3792              :     }
    3793              : 
    3794              :     /* Easy case if IGNORE NULLS is not specified */
    3795       118530 :     if (!null_treatment)
    3796              :     {
    3797              :         /* get tuple and evaluate in partition */
    3798       118290 :         datum = gettuple_eval_partition(winobj, argno,
    3799              :                                         abs_pos, isnull, &myisout);
    3800       118290 :         if (!myisout && set_mark)
    3801       118020 :             WinSetMarkPosition(winobj, abs_pos);
    3802       118290 :         if (isout)
    3803       118290 :             *isout = myisout;
    3804       118290 :         return datum;
    3805              :     }
    3806              : 
    3807              :     /* Prepare for loop */
    3808          240 :     notnull_offset = 0;
    3809          240 :     notnull_relpos = abs(relpos);
    3810          240 :     forward = relpos > 0 ? 1 : -1;
    3811          240 :     myisout = false;
    3812          240 :     datum = 0;
    3813              : 
    3814              :     /*
    3815              :      * IGNORE NULLS + WINDOW_SEEK_CURRENT + relpos > 0 case, we would fetch
    3816              :      * beyond the current row + relpos to find out the target row. If we mark
    3817              :      * at abs_pos, next call to WinGetFuncArgInPartition or
    3818              :      * WinGetFuncArgInFrame (in case when a window function have multiple
    3819              :      * args) could fail with "cannot fetch row before WindowObject's mark
    3820              :      * position". So keep the mark position at currentpos.
    3821              :      */
    3822          240 :     if (seektype == WINDOW_SEEK_CURRENT && relpos > 0)
    3823          120 :         mark_pos = winstate->currentpos;
    3824              :     else
    3825              :     {
    3826              :         /*
    3827              :          * For other cases we have no idea what position of row callers would
    3828              :          * fetch next time. Also for relpos < 0 case (we go backward), we
    3829              :          * cannot set mark either. For those cases we always set mark at 0.
    3830              :          */
    3831          120 :         mark_pos = 0;
    3832              :     }
    3833              : 
    3834              :     /*
    3835              :      * Get the next nonnull value in the partition, moving forward or backward
    3836              :      * until we find a value or reach the partition's end.  We cache the
    3837              :      * nullness status because we may repeat this process many times.
    3838              :      */
    3839              :     do
    3840              :     {
    3841              :         int         nn_info;    /* NOT NULL status */
    3842              : 
    3843          351 :         abs_pos += forward;
    3844          351 :         if (abs_pos < 0)     /* clearly out of partition */
    3845           39 :             break;
    3846              : 
    3847              :         /* check NOT NULL cached info */
    3848          312 :         nn_info = get_notnull_info(winobj, abs_pos, argno);
    3849          312 :         if (nn_info == NN_NOTNULL)  /* this row is known to be NOT NULL */
    3850           45 :             notnull_offset++;
    3851          267 :         else if (nn_info == NN_NULL)    /* this row is known to be NULL */
    3852           27 :             continue;           /* keep on moving forward or backward */
    3853              :         else                    /* need to check NULL or not */
    3854              :         {
    3855              :             /*
    3856              :              * NOT NULL info does not exist yet.  Get tuple and evaluate func
    3857              :              * arg in partition. We ignore the return value from
    3858              :              * gettuple_eval_partition because we are just interested in
    3859              :              * whether we are inside or outside of partition, NULL or NOT
    3860              :              * NULL.
    3861              :              */
    3862          240 :             (void) gettuple_eval_partition(winobj, argno,
    3863              :                                            abs_pos, isnull, &myisout);
    3864          240 :             if (myisout)        /* out of partition? */
    3865           30 :                 break;
    3866          210 :             if (!*isnull)
    3867          126 :                 notnull_offset++;
    3868              :             /* record the row status */
    3869          210 :             put_notnull_info(winobj, abs_pos, argno, *isnull);
    3870              :         }
    3871          282 :     } while (notnull_offset < notnull_relpos);
    3872              : 
    3873              :     /* get tuple and evaluate func arg in partition */
    3874          240 :     datum = gettuple_eval_partition(winobj, argno,
    3875              :                                     abs_pos, isnull, &myisout);
    3876          240 :     if (!myisout && set_mark)
    3877          171 :         WinSetMarkPosition(winobj, mark_pos);
    3878          240 :     if (isout)
    3879          240 :         *isout = myisout;
    3880              : 
    3881          240 :     return datum;
    3882              : }
    3883              : 
    3884              : /*
    3885              :  * WinGetFuncArgInFrame
    3886              :  *      Evaluate a window function's argument expression on a specified
    3887              :  *      row of the window frame.  The row is identified in lseek(2) style,
    3888              :  *      i.e. relative to the first or last row of the frame.  (We do not
    3889              :  *      support WINDOW_SEEK_CURRENT here, because it's not very clear what
    3890              :  *      that should mean if the current row isn't part of the frame.)
    3891              :  *
    3892              :  * argno: argument number to evaluate (counted from 0)
    3893              :  * relpos: signed rowcount offset from the seek position
    3894              :  * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
    3895              :  * set_mark: If the row is found/in frame and set_mark is true, the mark is
    3896              :  *      moved to the row as a side-effect.
    3897              :  * isnull: output argument, receives isnull status of result
    3898              :  * isout: output argument, set to indicate whether target row position
    3899              :  *      is out of frame (can pass NULL if caller doesn't care about this)
    3900              :  *
    3901              :  * Specifying a nonexistent or not-in-frame row is not an error, it just
    3902              :  * causes a null result (plus setting *isout true, if isout isn't NULL).
    3903              :  *
    3904              :  * Note that some exclusion-clause options lead to situations where the
    3905              :  * rows that are in-frame are not consecutive in the partition.  But we
    3906              :  * count only in-frame rows when measuring relpos.
    3907              :  *
    3908              :  * The set_mark flag is interpreted as meaning that the caller will specify
    3909              :  * a constant (or, perhaps, monotonically increasing) relpos in successive
    3910              :  * calls, so that *if there is no exclusion clause* there will be no need
    3911              :  * to fetch a row before the previously fetched row.  But we do not expect
    3912              :  * the caller to know how to account for exclusion clauses.  Therefore,
    3913              :  * if there is an exclusion clause we take responsibility for adjusting the
    3914              :  * mark request to something that will be safe given the above assumption
    3915              :  * about relpos.
    3916              :  */
    3917              : Datum
    3918         4974 : WinGetFuncArgInFrame(WindowObject winobj, int argno,
    3919              :                      int relpos, int seektype, bool set_mark,
    3920              :                      bool *isnull, bool *isout)
    3921              : {
    3922              :     WindowAggState *winstate;
    3923              :     ExprContext *econtext;
    3924              :     TupleTableSlot *slot;
    3925              :     int64       abs_pos;
    3926              :     int64       mark_pos;
    3927              : 
    3928              :     Assert(WindowObjectIsValid(winobj));
    3929         4974 :     winstate = winobj->winstate;
    3930         4974 :     econtext = winstate->ss.ps.ps_ExprContext;
    3931         4974 :     slot = winstate->temp_slot_1;
    3932              : 
    3933         4974 :     if (winobj->ignore_nulls == IGNORE_NULLS)
    3934          480 :         return ignorenulls_getfuncarginframe(winobj, argno, relpos, seektype,
    3935              :                                              set_mark, isnull, isout);
    3936              : 
    3937         4494 :     switch (seektype)
    3938              :     {
    3939            0 :         case WINDOW_SEEK_CURRENT:
    3940            0 :             elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for WinGetFuncArgInFrame");
    3941              :             abs_pos = mark_pos = 0; /* keep compiler quiet */
    3942              :             break;
    3943         2223 :         case WINDOW_SEEK_HEAD:
    3944              :             /* rejecting relpos < 0 is easy and simplifies code below */
    3945         2223 :             if (relpos < 0)
    3946            0 :                 goto out_of_frame;
    3947         2223 :             update_frameheadpos(winstate);
    3948         2202 :             abs_pos = winstate->frameheadpos + relpos;
    3949         2202 :             mark_pos = abs_pos;
    3950              : 
    3951              :             /*
    3952              :              * Account for exclusion option if one is active, but advance only
    3953              :              * abs_pos not mark_pos.  This prevents changes of the current
    3954              :              * row's peer group from resulting in trying to fetch a row before
    3955              :              * some previous mark position.
    3956              :              *
    3957              :              * Note that in some corner cases such as current row being
    3958              :              * outside frame, these calculations are theoretically too simple,
    3959              :              * but it doesn't matter because we'll end up deciding the row is
    3960              :              * out of frame.  We do not attempt to avoid fetching rows past
    3961              :              * end of frame; that would happen in some cases anyway.
    3962              :              */
    3963         2202 :             switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
    3964              :             {
    3965         1872 :                 case 0:
    3966              :                     /* no adjustment needed */
    3967         1872 :                     break;
    3968          120 :                 case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
    3969          120 :                     if (abs_pos >= winstate->currentpos &&
    3970           93 :                         winstate->currentpos >= winstate->frameheadpos)
    3971           33 :                         abs_pos++;
    3972          120 :                     break;
    3973           60 :                 case FRAMEOPTION_EXCLUDE_GROUP:
    3974           60 :                     update_grouptailpos(winstate);
    3975           60 :                     if (abs_pos >= winstate->groupheadpos &&
    3976           36 :                         winstate->grouptailpos > winstate->frameheadpos)
    3977              :                     {
    3978           36 :                         int64       overlapstart = Max(winstate->groupheadpos,
    3979              :                                                        winstate->frameheadpos);
    3980              : 
    3981           36 :                         abs_pos += winstate->grouptailpos - overlapstart;
    3982              :                     }
    3983           60 :                     break;
    3984          150 :                 case FRAMEOPTION_EXCLUDE_TIES:
    3985          150 :                     update_grouptailpos(winstate);
    3986          150 :                     if (abs_pos >= winstate->groupheadpos &&
    3987          102 :                         winstate->grouptailpos > winstate->frameheadpos)
    3988              :                     {
    3989           42 :                         int64       overlapstart = Max(winstate->groupheadpos,
    3990              :                                                        winstate->frameheadpos);
    3991              : 
    3992           42 :                         if (abs_pos == overlapstart)
    3993           42 :                             abs_pos = winstate->currentpos;
    3994              :                         else
    3995            0 :                             abs_pos += winstate->grouptailpos - overlapstart - 1;
    3996              :                     }
    3997          150 :                     break;
    3998            0 :                 default:
    3999            0 :                     elog(ERROR, "unrecognized frame option state: 0x%x",
    4000              :                          winstate->frameOptions);
    4001              :                     break;
    4002              :             }
    4003         2202 :             break;
    4004         2271 :         case WINDOW_SEEK_TAIL:
    4005              :             /* rejecting relpos > 0 is easy and simplifies code below */
    4006         2271 :             if (relpos > 0)
    4007            0 :                 goto out_of_frame;
    4008         2271 :             update_frametailpos(winstate);
    4009         2268 :             abs_pos = winstate->frametailpos - 1 + relpos;
    4010              : 
    4011              :             /*
    4012              :              * Account for exclusion option if one is active.  If there is no
    4013              :              * exclusion, we can safely set the mark at the accessed row.  But
    4014              :              * if there is, we can only mark the frame start, because we can't
    4015              :              * be sure how far back in the frame the exclusion might cause us
    4016              :              * to fetch in future.  Furthermore, we have to actually check
    4017              :              * against frameheadpos here, since it's unsafe to try to fetch a
    4018              :              * row before frame start if the mark might be there already.
    4019              :              */
    4020         2268 :             switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
    4021              :             {
    4022         2028 :                 case 0:
    4023              :                     /* no adjustment needed */
    4024         2028 :                     mark_pos = abs_pos;
    4025         2028 :                     break;
    4026           60 :                 case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
    4027           60 :                     if (abs_pos <= winstate->currentpos &&
    4028            6 :                         winstate->currentpos < winstate->frametailpos)
    4029            6 :                         abs_pos--;
    4030           60 :                     update_frameheadpos(winstate);
    4031           60 :                     if (abs_pos < winstate->frameheadpos)
    4032            3 :                         goto out_of_frame;
    4033           57 :                     mark_pos = winstate->frameheadpos;
    4034           57 :                     break;
    4035          120 :                 case FRAMEOPTION_EXCLUDE_GROUP:
    4036          120 :                     update_grouptailpos(winstate);
    4037          120 :                     if (abs_pos < winstate->grouptailpos &&
    4038           27 :                         winstate->groupheadpos < winstate->frametailpos)
    4039              :                     {
    4040           27 :                         int64       overlapend = Min(winstate->grouptailpos,
    4041              :                                                      winstate->frametailpos);
    4042              : 
    4043           27 :                         abs_pos -= overlapend - winstate->groupheadpos;
    4044              :                     }
    4045          120 :                     update_frameheadpos(winstate);
    4046          120 :                     if (abs_pos < winstate->frameheadpos)
    4047           27 :                         goto out_of_frame;
    4048           93 :                     mark_pos = winstate->frameheadpos;
    4049           93 :                     break;
    4050           60 :                 case FRAMEOPTION_EXCLUDE_TIES:
    4051           60 :                     update_grouptailpos(winstate);
    4052           60 :                     if (abs_pos < winstate->grouptailpos &&
    4053           18 :                         winstate->groupheadpos < winstate->frametailpos)
    4054              :                     {
    4055           18 :                         int64       overlapend = Min(winstate->grouptailpos,
    4056              :                                                      winstate->frametailpos);
    4057              : 
    4058           18 :                         if (abs_pos == overlapend - 1)
    4059           18 :                             abs_pos = winstate->currentpos;
    4060              :                         else
    4061            0 :                             abs_pos -= overlapend - 1 - winstate->groupheadpos;
    4062              :                     }
    4063           60 :                     update_frameheadpos(winstate);
    4064           60 :                     if (abs_pos < winstate->frameheadpos)
    4065            0 :                         goto out_of_frame;
    4066           60 :                     mark_pos = winstate->frameheadpos;
    4067           60 :                     break;
    4068            0 :                 default:
    4069            0 :                     elog(ERROR, "unrecognized frame option state: 0x%x",
    4070              :                          winstate->frameOptions);
    4071              :                     mark_pos = 0;   /* keep compiler quiet */
    4072              :                     break;
    4073              :             }
    4074         2238 :             break;
    4075            0 :         default:
    4076            0 :             elog(ERROR, "unrecognized window seek type: %d", seektype);
    4077              :             abs_pos = mark_pos = 0; /* keep compiler quiet */
    4078              :             break;
    4079              :     }
    4080              : 
    4081         4440 :     if (!window_gettupleslot(winobj, abs_pos, slot))
    4082          198 :         goto out_of_frame;
    4083              : 
    4084              :     /* The code above does not detect all out-of-frame cases, so check */
    4085         4242 :     if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
    4086          150 :         goto out_of_frame;
    4087              : 
    4088         4077 :     if (isout)
    4089            0 :         *isout = false;
    4090         4077 :     if (set_mark)
    4091         4056 :         WinSetMarkPosition(winobj, mark_pos);
    4092         4077 :     econtext->ecxt_outertuple = slot;
    4093         4077 :     return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
    4094              :                         econtext, isnull);
    4095              : 
    4096          378 : out_of_frame:
    4097          378 :     if (isout)
    4098            0 :         *isout = true;
    4099          378 :     *isnull = true;
    4100          378 :     return (Datum) 0;
    4101              : }
    4102              : 
    4103              : /*
    4104              :  * WinGetFuncArgCurrent
    4105              :  *      Evaluate a window function's argument expression on the current row.
    4106              :  *
    4107              :  * argno: argument number to evaluate (counted from 0)
    4108              :  * isnull: output argument, receives isnull status of result
    4109              :  *
    4110              :  * Note: this isn't quite equivalent to WinGetFuncArgInPartition or
    4111              :  * WinGetFuncArgInFrame targeting the current row, because it will succeed
    4112              :  * even if the WindowObject's mark has been set beyond the current row.
    4113              :  * This should generally be used for "ordinary" arguments of a window
    4114              :  * function, such as the offset argument of lead() or lag().
    4115              :  */
    4116              : Datum
    4117         1005 : WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
    4118              : {
    4119              :     WindowAggState *winstate;
    4120              :     ExprContext *econtext;
    4121              : 
    4122              :     Assert(WindowObjectIsValid(winobj));
    4123         1005 :     winstate = winobj->winstate;
    4124              : 
    4125         1005 :     econtext = winstate->ss.ps.ps_ExprContext;
    4126              : 
    4127         1005 :     econtext->ecxt_outertuple = winstate->ss.ss_ScanTupleSlot;
    4128         1005 :     return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
    4129              :                         econtext, isnull);
    4130              : }
        

Generated by: LCOV version 2.0-1