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

Generated by: LCOV version 1.14