Hi.

based on https://github.com/assam258-5892/postgres/tree/RPR-20260930
I have made some non-trivial refactoring in nodeWindowAgg.c, see attached.

The attached patch commit message is as follows:
================
    Separate resolving the reduced frame from consuming it

    ExecWindowAgg() resolves the reduced frame for every row it emits,
by calling
    ensure_reduced_frame() before any window function or aggregate
runs; that is the
    only place the match is driven.  Everything after it should only
consumes the
    recorded match through get_reduced_frame_status(), which reports how any row
    position relates to it.

    row_is_in_reduced_frame should be removed.

    row_is_in_reduced_frame() blurred that division: it re-ran
    ensure_reduced_frame() and then get_reduced_frame_status() again,
so consumers
    looked as if they might resolve the match themselves.

    While at it, I found the change to WinGetFuncArgInFrame() hard to
review: the
    new WinGetSlotInFrame() mixes RPR logic into the existing frame
code.  IGNORE
    NULLS already it's own function: ignorenulls_getfuncarginframe().
Similarly we
    can left WinGetFuncArgInFrame() unchanged (same as the master
branch) and moved the RPR logic into a
    dedicated function, WinGetFuncArgInReducedFrame().  That
duplicates some code,
    but the result is much cleaner, and I think the duplication is worth it.

================



--
jian
https://www.enterprisedb.com/
From 6be6ca9bdfb97f8f5fd9052bc9b8d3eea61e4549 Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Wed, 21 Oct 2026 14:33:38 +0800
Subject: [PATCH v55 1/1] Separate resolving the reduced frame from consuming
 it

ExecWindowAgg() resolves the reduced frame for every row it emits, by calling
ensure_reduced_frame() before any window function or aggregate runs; that is the
only place the match is driven.  Everything after it should only consumes the
recorded match through get_reduced_frame_status(), which reports how any row
position relates to it.

row_is_in_reduced_frame should be removed.

row_is_in_reduced_frame() blurred that division: it re-ran
ensure_reduced_frame() and then get_reduced_frame_status() again, so consumers
looked as if they might resolve the match themselves.

While at it, I found the change to WinGetFuncArgInFrame() hard to review: the
new WinGetSlotInFrame() mixes RPR logic into the existing frame code.  IGNORE
NULLS already it's own function: ignorenulls_getfuncarginframe().  Similarly we
can left WinGetFuncArgInFrame() unchanged and moved the RPR logic into a
dedicated function, WinGetFuncArgInReducedFrame().  That duplicates some code,
but the result is much cleaner, and I think the duplication is worth it.
---
 src/backend/executor/README.rpr      |  77 ++--
 src/backend/executor/nodeWindowAgg.c | 576 ++++++++++++---------------
 2 files changed, 300 insertions(+), 353 deletions(-)

diff --git a/src/backend/executor/README.rpr b/src/backend/executor/README.rpr
index 16e158fdcf..7612bada04 100644
--- a/src/backend/executor/README.rpr
+++ b/src/backend/executor/README.rpr
@@ -1265,14 +1265,20 @@ Chapter VII  NFA Execution: 3-Phase Model
 
 VII-1. Entry Point and Overall Flow
 
-ExecWindowAgg() drives the match once for every row of the scan by
-calling ensure_reduced_frame(), so the match tracks the row scan rather
-than frame access.  That function is idempotent: it calls
-update_frameheadpos() and update_reduced_frame() only when
-get_reduced_frame_status() reports the row as RF_NOT_DETERMINED.
-row_is_in_reduced_frame(), which the frame access paths call to classify
-a row, goes through ensure_reduced_frame() first and then reads the
-recorded result.
+ExecWindowAgg() resolves the reduced frame for every row it emits, by
+calling ensure_reduced_frame() before any window function or aggregate
+runs.  This is the *only* place the match is driven.
+
+The labor is split in two: ExecWindowAgg() produces the reduced frame range, 
and
+everything downstream only consumes it.  The aggregate loop in
+eval_windowaggregates() and the frame access in WinGetFuncArgInReducedFrame() 
call
+get_reduced_frame_status() to classify a row against the reduced frame range,
+and must never resolve or modify it themselves.
+
+Driving the match from ExecWindowAgg() also keeps it in step with the row scan.
+The RPR machinery expects rows to be resolved in order; if resolution were left
+to frame access, a function that never reads its frame (nth_value() with a NULL
+offset) would skip a row and leave the match state one row behind.
 
 Flow of update_reduced_frame():
 
@@ -2145,30 +2151,22 @@ not been evaluated yet (RF_NOT_DETERMINED).
 A row's status against the current match result can be obtained by
 calling get_reduced_frame_status().
 
-The frame accessors in nodeWindowAgg.c -- WinGetSlotInFrame() and
-ignorenulls_getfuncarginframe(), the latter serving IGNORE NULLS --
-clip every seek to the reduced frame.  Each asks
-row_is_in_reduced_frame() about frameheadpos and takes the length it
-returns as the frame's extent: a WINDOW_SEEK_HEAD relpos at or past
-that length is out of frame, as is a WINDOW_SEEK_TAIL relpos whose
-backward distance reaches it, the tail case landing on
-frameheadpos + relpos + length - 1.  That arithmetic assumes the reduced
-frame is one contiguous run beginning at frameheadpos, which holds only
-because RPR rejects EXCLUDE (III-1) and pins the frame start at
-CURRENT ROW; relaxing either restriction means revisiting both
-accessors.  The same guarantee is why a WINDOW_SEEK_TAIL access under
-RPR marks frameheadpos rather than the accessed row: the frame start
-never moves backwards, so the mark can never end up ahead of a row a
-later fetch still needs.
+The frame accessors clip every seek to the reduced frame, the rows
+[frameheadpos, frameheadpos + match length).  That arithmetic assumes the
+reduced frame is one contiguous run starting at frameheadpos, which holds only
+because RPR rejects EXCLUDE and pins the frame start at CURRENT ROW; relaxing
+either restriction means revisiting both accessors: WinGetFuncArgInFrame()
+hands an RPR window to WinGetFuncArgInReducedFrame(), and IGNORE NULLS goes
+through ignorenulls_getfuncarginframe().  The same guarantee lets a
+WINDOW_SEEK_TAIL access under RPR mark frameheadpos rather than the accessed
+row: the frame start never moves backwards, so the mark cannot get ahead of a
+row a later fetch needs.
 
 An empty match is still recorded as a success: update_reduced_frame()
 passes it to ExecRPRRecordContextSuccess() with length 0, so EXPLAIN
-counts it under NFA Matched.  The frame accessors never see that 0,
-though.  row_is_in_reduced_frame() returns -1 for RF_EMPTY_MATCH exactly
-as for RF_UNMATCHED, so the row's reduced frame is empty.  It cannot
-return 0 there: 0 is its "RPR not defined" answer, which the accessors
-and the aggregation loop of XI-5 read as the unclipped full frame.
-RF_FRAME_HEAD returns the match length (>= 1) and RF_SKIPPED returns -2.
+counts it under NFA Matched.  For the frame accessors it is no different
+from no match: get_reduced_frame_status() reports RF_EMPTY_MATCH, not
+RF_FRAME_HEAD, so the row's reduced frame is empty.
 
 XI-2. AFTER MATCH SKIP
 
@@ -2245,15 +2243,18 @@ XI-4. Bounded Frame Handling
 
 XI-5. Window Aggregates over the Reduced Frame
 
-  RPR switches off the moving-aggregate path.  eval_windowaggregates()
-  marks every plain aggregate for restart on every row when
-  rpr_is_defined(), because one row's reduced frame need not overlap the
-  next row's at all, and overlap is the assumption an inverse transition
-  function rests on.  The aggregation loop then reads the reduced frame
-  itself to find where to stop: it stops when currentpos is determined
-  but aggregatedupto is not, when row_is_in_reduced_frame() reports an
-  unmatched row, or when the base row of the aggregation turns out to be
-  an interior (RF_SKIPPED) row of a match.
+  RPR switches off the moving-aggregate path.  Normally, when the
+  current row moves from N to N+1, the new frame is the old frame minus
+  a few rows at the front plus a few rows at the end.  PostgreSQL
+  exploits that: instead of recomputing the aggregate, it subtracts the
+  dropped rows with the aggregate's inverse transition function and adds
+  the new ones.
+
+  With RPR the frame of row N+1 is the match that starts at row N+1,
+  which can be any set of rows; it is not the frame of row N with some
+  rows dropped.  There is nothing to subtract, so
+  eval_windowaggregates() restarts every plain aggregate on every row
+  when rpr_is_defined().
 
 Chapter XII  Worked Example: Full Execution Trace
 ============================================================================
diff --git a/src/backend/executor/nodeWindowAgg.c 
b/src/backend/executor/nodeWindowAgg.c
index 01cec0f849..8d76b31ef9 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -227,9 +227,10 @@ static Datum GetAggInitVal(Datum textInitVal, Oid 
transtype);
 
 static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
                                          TupleTableSlot *slot2);
-static int     WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot,
-                                                         int relpos, int 
seektype, bool set_mark,
-                                                         bool *isnull, bool 
*isout);
+static Datum WinGetFuncArgInReducedFrame(WindowObject winobj, int argno,
+                                                                               
 int relpos, int seektype,
+                                                                               
 bool set_mark, bool *isnull,
+                                                                               
 bool *isout);
 static bool window_gettupleslot(WindowObject winobj, int64 pos,
                                                                TupleTableSlot 
*slot);
 
@@ -249,7 +250,6 @@ static uint8 get_notnull_info(WindowObject winobj,
 static void put_notnull_info(WindowObject winobj,
                                                         int64 pos, int argno, 
bool isnull);
 static bool rpr_is_defined(WindowAggState *winstate);
-static int64 row_is_in_reduced_frame(WindowObject winobj, int64 pos);
 static void ensure_reduced_frame(WindowObject winobj, int64 pos);
 
 static void clear_reduced_frame(WindowAggState *winstate);
@@ -1048,32 +1048,44 @@ eval_windowaggregates(WindowAggState *winstate)
 
                if (rpr_is_defined(winstate))
                {
+                       int                     status;
+
                        /*
-                        * If currentpos is already decided but aggregatedupto 
is not yet
-                        * determined, we've passed the last reduced frame.
+                        * ExecWindowAgg already resolved the current row's 
match status
                         */
-                       if (get_reduced_frame_status(winstate, 
winstate->currentpos)
-                               != RF_NOT_DETERMINED &&
-                               get_reduced_frame_status(winstate, 
winstate->aggregatedupto)
-                               == RF_NOT_DETERMINED)
-                               break;
+                       Assert(get_reduced_frame_status(winstate, 
winstate->currentpos) !=
+                                  RF_NOT_DETERMINED);
+
+                       status = get_reduced_frame_status(winstate,
+                                                                               
          winstate->aggregatedupto);
 
                        /*
-                        * Calculate the reduced frame for aggregatedupto.
+                        * Exit loop once aggregatedupto leaves the current 
row's reduced
+                        * frame: it has passed the match's last row, or the 
current row
+                        * begins no match at all.
                         */
-                       ret = row_is_in_reduced_frame(winstate->agg_winobj,
-                                                                               
  winstate->aggregatedupto);
-                       if (ret == -1)          /* unmatched row */
+                       if (status == RF_NOT_DETERMINED ||
+                               status == RF_UNMATCHED ||
+                               status == RF_EMPTY_MATCH)
                                break;
 
                        /*
-                        * Check if current row is inside a match but not the 
head
-                        * (skipped), and it's the base row for aggregation.
+                        * The current row may lie inside a match that an 
earlier row
+                        * started.  We still get here for it, because every 
output row
+                        * computes its own aggregates and row_is_in_frame() 
above checks
+                        * only the physical frame.  Such a row cannot start a 
match of
+                        * its own, so its reduced frame is empty and nothing is
+                        * aggregated.
+                        *
+                        * Only the current row needs this check, and it is the 
row being
+                        * looked at while nothing has been aggregated yet, 
that is, while
+                        * aggregatedupto still equals aggregatedbase.
                         */
-                       if (get_reduced_frame_status(winstate,
-                                                                               
 winstate->aggregatedupto) == RF_SKIPPED &&
+                       if (status == RF_SKIPPED &&
                                winstate->aggregatedupto == 
winstate->aggregatedbase)
                                break;
+
+                       /* RF_FRAME_HEAD, or a later row of the match: 
aggregate it */
                }
 
                /* Set tuple context for evaluation of aggregate arguments */
@@ -2567,6 +2579,8 @@ ExecWindowAgg(PlanState *pstate)
                                elog(ERROR, "unexpected end of tuplestore");
                }
 
+               Assert(!rpr_is_defined(winstate) || winstate->status == 
WINDOWAGG_RUN);
+
                /* don't evaluate the window functions when we're in 
pass-through mode */
                if (winstate->status == WINDOWAGG_RUN)
                {
@@ -2580,10 +2594,9 @@ ExecWindowAgg(PlanState *pstate)
                                        clear_reduced_frame(winstate);
 
                                /*
-                                * Drive the row pattern match every row, so it 
tracks the row
-                                * scan rather than frame access: a window 
function that skips
-                                * the frame (e.g. nth_value() with a NULL 
offset) must not
-                                * leave the match state behind currentpos.
+                                * Resolve the current row's match here, once 
per row and
+                                * before any window function or aggregate 
runs.  This is the
+                                * only place the match is driven.
                                 */
                                Assert(winstate->nav_winobj != NULL);
                                ensure_reduced_frame(winstate->nav_winobj,
@@ -3819,13 +3832,6 @@ ignorenulls_getfuncarginframe(WindowObject winobj, int 
argno,
                        /* rejecting relpos > 0 is easy and simplifies code 
below */
                        if (relpos > 0)
                                goto out_of_frame;
-
-                       /*
-                        * RPR cares about frame head pos. Need to call
-                        * update_frameheadpos
-                        */
-                       update_frameheadpos(winstate);
-
                        update_frametailpos(winstate);
                        abs_pos = winstate->frametailpos - 1;
                        mark_pos = 0;           /* keep compiler quiet */
@@ -3838,36 +3844,33 @@ ignorenulls_getfuncarginframe(WindowObject winobj, int 
argno,
        }
 
        /*
-        * Get the next nonnull value in the frame, moving forward or backward
-        * until we find a value or reach the frame's end.
+        * Under RPR, search only the reduced frame: the match that starts at 
the
+        * current row, if any.
         */
-
-       /*
-        * Check whether current row is in reduced frame.
-        */
-       num_reduced_frame = row_is_in_reduced_frame(winobj, 
winstate->frameheadpos);
-       if (num_reduced_frame < 0)      /* unmatched or skipped row */
-               goto out_of_frame;
-       else if (num_reduced_frame > 0) /* the first row of the reduced frame */
+       if (rpr_is_defined(winstate))
        {
-               /*
-                * Early check if row could be out of reduced frame.  When RPR 
is
-                * enabled, EXCLUDE clause cannot be specified and the frame is 
always
-                * contiguous.  So we can safely perform the following checks. 
Note,
-                * however, it is possible that a row is out of reduced frame if
-                * there's a NULL in the middle. So we need to check it in the
-                * following do loop.
-                */
-               if (seektype == WINDOW_SEEK_HEAD && relpos >= num_reduced_frame)
+               int                     status;
+
+               /* both seek types need the frame head of the reduced frame */
+               update_frameheadpos(winstate);
+
+               status = get_reduced_frame_status(winstate, 
winstate->frameheadpos);
+
+               /* ExecWindowAgg should already resolved the reduced frame */
+               Assert(status != RF_NOT_DETERMINED);
+
+               if (status != RF_FRAME_HEAD)
                        goto out_of_frame;
-               if (seektype == WINDOW_SEEK_TAIL)
-               {
-                       if (notnull_relpos >= num_reduced_frame)
-                               goto out_of_frame;
 
-                       /* not out of reduced frame. Set abspos as a starting 
point */
+               num_reduced_frame = winstate->rpr_match_length;
+
+               /* the reduced frame has only num_reduced_frame rows */
+               if (notnull_relpos >= num_reduced_frame)
+                       goto out_of_frame;
+
+               /* a tail search starts at the reduced frame's last row */
+               if (seektype == WINDOW_SEEK_TAIL)
                        abs_pos = winstate->frameheadpos + num_reduced_frame - 
1;
-               }
        }
 
        do
@@ -4461,69 +4464,6 @@ rpr_is_defined(WindowAggState *winstate)
        return winstate->rpPattern != NULL;
 }
 
-/*
- * -----------------
- * row_is_in_reduced_frame
- * Determine whether a row is in the current row's reduced window frame
- * according to row pattern matching
- *
- * If pos is not yet determined, the match is first driven forward by
- * ensure_reduced_frame().
- *
- * Returns:
- * = 0, RPR is not defined.
- * >0, if the row is the first in the reduced frame. Return the number of rows
- * in the reduced frame.
- * -1, if the row is unmatched or starts an empty match
- * -2, if the row is inside the current match but is not its first row (an
- * interior row of the match)
- * -----------------
- */
-static int64
-row_is_in_reduced_frame(WindowObject winobj, int64 pos)
-{
-       WindowAggState *winstate = winobj->winstate;
-       int                     state;
-       int64           rtn;
-
-       if (!rpr_is_defined(winstate))
-       {
-               /*
-                * RPR is not defined. Assume that we are always in the reduced 
window
-                * frame.
-                */
-               rtn = 0;
-               return rtn;
-       }
-
-       ensure_reduced_frame(winobj, pos);
-
-       state = get_reduced_frame_status(winstate, pos);
-
-       switch (state)
-       {
-               case RF_FRAME_HEAD:
-                       rtn = winstate->rpr_match_length;
-                       break;
-
-               case RF_SKIPPED:
-                       rtn = -2;
-                       break;
-
-               case RF_UNMATCHED:
-               case RF_EMPTY_MATCH:
-                       rtn = -1;
-                       break;
-
-               default:
-                       elog(ERROR, "unrecognized state: %d at: " INT64_FORMAT,
-                                state, pos);
-                       break;
-       }
-
-       return rtn;
-}
-
 /*
  * ensure_reduced_frame
  *             Drive the row pattern match forward so pos is resolved.
@@ -4900,231 +4840,80 @@ rpr_prepare_row(WindowObject winobj, int64 pos, 
RPRVarMatch *varMatched)
 }
 
 /*
- * WinGetSlotInFrame
- * slot: TupleTableSlot to store the result
- * relpos: signed rowcount offset from the seek position
- * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
- * set_mark: If the row is found/in frame and set_mark is true, the mark is
- *             moved to the row as a side-effect.
- * isnull: output argument, receives isnull status of result
- * isout: output argument, set to indicate whether target row position
- *             is out of frame (can pass NULL if caller doesn't care about 
this)
+ * WinGetFuncArgInReducedFrame
+ *             WinGetFuncArgInFrame() for a row pattern window.
  *
- * Returns 0 if we successfully got the slot, or nonzero if out of frame.
- * (isout is also set in the latter case.)
+ * A row pattern frame starts at CURRENT ROW and allows no EXCLUDE, so the
+ * reduced frame is the contiguous run [frameheadpos, frameheadpos + length)
+ * of the match that starts at the current row.  ExecWindowAgg() has already
+ * resolved that match.
  */
-static int
-WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot,
-                                 int relpos, int seektype, bool set_mark,
-                                 bool *isnull, bool *isout)
+static Datum
+WinGetFuncArgInReducedFrame(WindowObject winobj, int argno,
+                                                       int relpos, int 
seektype, bool set_mark,
+                                                       bool *isnull, bool 
*isout)
 {
-       WindowAggState *winstate;
-       int64           abs_pos;
-       int64           mark_pos;
-       int64           num_reduced_frame;
+       WindowAggState *winstate = winobj->winstate;
+       ExprContext *econtext = winstate->ss.ps.ps_ExprContext;
+       TupleTableSlot *slot = winstate->temp_slot_1;
+       int                     status;
+       int64           length;
+       int64           abs_pos = 0;
+       int64           mark_pos = 0;
 
-       Assert(WindowObjectIsValid(winobj));
-       winstate = winobj->winstate;
+       update_frameheadpos(winstate);
+       status = get_reduced_frame_status(winstate,
+                                                                         
winstate->frameheadpos);
+
+       Assert(status != RF_NOT_DETERMINED);
+
+       /* only a match head has a reduced frame */
+       if (status != RF_FRAME_HEAD)
+               goto out_of_frame;
+
+       length = winstate->rpr_match_length;
 
        switch (seektype)
        {
                case WINDOW_SEEK_CURRENT:
                        elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for 
WinGetFuncArgInFrame");
-                       abs_pos = mark_pos = 0; /* keep compiler quiet */
                        break;
                case WINDOW_SEEK_HEAD:
-                       /* rejecting relpos < 0 is easy and simplifies code 
below */
-                       if (relpos < 0)
-                               goto out_of_frame;
-                       update_frameheadpos(winstate);
-                       abs_pos = winstate->frameheadpos + relpos;
-                       mark_pos = abs_pos;
-
-                       /*
-                        * Account for exclusion option if one is active, but 
advance only
-                        * abs_pos not mark_pos.  This prevents changes of the 
current
-                        * row's peer group from resulting in trying to fetch a 
row before
-                        * some previous mark position.
-                        *
-                        * Note that in some corner cases such as current row 
being
-                        * outside frame, these calculations are theoretically 
too simple,
-                        * but it doesn't matter because we'll end up deciding 
the row is
-                        * out of frame.  We do not attempt to avoid fetching 
rows past
-                        * end of frame; that would happen in some cases anyway.
-                        */
-                       switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
-                       {
-                               case 0:
-                                       /* no adjustment needed */
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
-                                       if (abs_pos >= winstate->currentpos &&
-                                               winstate->currentpos >= 
winstate->frameheadpos)
-                                               abs_pos++;
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_GROUP:
-                                       update_grouptailpos(winstate);
-                                       if (abs_pos >= winstate->groupheadpos &&
-                                               winstate->grouptailpos > 
winstate->frameheadpos)
-                                       {
-                                               int64           overlapstart = 
Max(winstate->groupheadpos,
-                                                                               
                           winstate->frameheadpos);
-
-                                               abs_pos += 
winstate->grouptailpos - overlapstart;
-                                       }
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_TIES:
-                                       update_grouptailpos(winstate);
-                                       if (abs_pos >= winstate->groupheadpos &&
-                                               winstate->grouptailpos > 
winstate->frameheadpos)
-                                       {
-                                               int64           overlapstart = 
Max(winstate->groupheadpos,
-                                                                               
                           winstate->frameheadpos);
-
-                                               if (abs_pos == overlapstart)
-                                                       abs_pos = 
winstate->currentpos;
-                                               else
-                                                       abs_pos += 
winstate->grouptailpos - overlapstart - 1;
-                                       }
-                                       break;
-                               default:
-                                       elog(ERROR, "unrecognized frame option 
state: 0x%x",
-                                                winstate->frameOptions);
-                                       break;
-                       }
-                       num_reduced_frame = row_is_in_reduced_frame(winobj,
-                                                                               
                                winstate->frameheadpos);
-                       if (num_reduced_frame < 0)
+                       if (relpos < 0 || relpos >= length)
                                goto out_of_frame;
-                       else if (num_reduced_frame > 0)
-                               if (relpos >= num_reduced_frame)
-                                       goto out_of_frame;
+                       abs_pos = mark_pos = winstate->frameheadpos + relpos;
                        break;
                case WINDOW_SEEK_TAIL:
-                       /* rejecting relpos > 0 is easy and simplifies code 
below */
-                       if (relpos > 0)
+                       if (relpos > 0 || relpos <= -length)
                                goto out_of_frame;
-
-                       /*
-                        * RPR cares about frame head pos. Need to call
-                        * update_frameheadpos
-                        */
-                       update_frameheadpos(winstate);
-
-                       update_frametailpos(winstate);
-                       abs_pos = winstate->frametailpos - 1 + relpos;
-
-                       /*
-                        * Account for exclusion option if one is active.  If 
there is no
-                        * exclusion, we can safely set the mark at the 
accessed row.  But
-                        * if there is, we can only mark the frame start, 
because we can't
-                        * be sure how far back in the frame the exclusion 
might cause us
-                        * to fetch in future.  Furthermore, we have to 
actually check
-                        * against frameheadpos here, since it's unsafe to try 
to fetch a
-                        * row before frame start if the mark might be there 
already.
-                        */
-                       switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
-                       {
-                               case 0:
-                                       /* no adjustment needed */
-                                       mark_pos = abs_pos;
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
-                                       if (abs_pos <= winstate->currentpos &&
-                                               winstate->currentpos < 
winstate->frametailpos)
-                                               abs_pos--;
-                                       update_frameheadpos(winstate);
-                                       if (abs_pos < winstate->frameheadpos)
-                                               goto out_of_frame;
-                                       mark_pos = winstate->frameheadpos;
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_GROUP:
-                                       update_grouptailpos(winstate);
-                                       if (abs_pos < winstate->grouptailpos &&
-                                               winstate->groupheadpos < 
winstate->frametailpos)
-                                       {
-                                               int64           overlapend = 
Min(winstate->grouptailpos,
-                                                                               
                         winstate->frametailpos);
-
-                                               abs_pos -= overlapend - 
winstate->groupheadpos;
-                                       }
-                                       update_frameheadpos(winstate);
-                                       if (abs_pos < winstate->frameheadpos)
-                                               goto out_of_frame;
-                                       mark_pos = winstate->frameheadpos;
-                                       break;
-                               case FRAMEOPTION_EXCLUDE_TIES:
-                                       update_grouptailpos(winstate);
-                                       if (abs_pos < winstate->grouptailpos &&
-                                               winstate->groupheadpos < 
winstate->frametailpos)
-                                       {
-                                               int64           overlapend = 
Min(winstate->grouptailpos,
-                                                                               
                         winstate->frametailpos);
-
-                                               if (abs_pos == overlapend - 1)
-                                                       abs_pos = 
winstate->currentpos;
-                                               else
-                                                       abs_pos -= overlapend - 
1 - winstate->groupheadpos;
-                                       }
-                                       update_frameheadpos(winstate);
-                                       if (abs_pos < winstate->frameheadpos)
-                                               goto out_of_frame;
-                                       mark_pos = winstate->frameheadpos;
-                                       break;
-                               default:
-                                       elog(ERROR, "unrecognized frame option 
state: 0x%x",
-                                                winstate->frameOptions);
-                                       mark_pos = 0;   /* keep compiler quiet 
*/
-                                       break;
-                       }
-
-                       num_reduced_frame = row_is_in_reduced_frame(winobj,
-                                                                               
                                winstate->frameheadpos);
-                       /* zero means a non-RPR window, which has no reduced 
frame */
-                       if (num_reduced_frame < 0)
-                               goto out_of_frame;
-                       else if (num_reduced_frame > 0)
-                       {
-                               if (-relpos >= num_reduced_frame)
-                                       goto out_of_frame;
-                               abs_pos = winstate->frameheadpos + relpos +
-                                       num_reduced_frame - 1;
-                       }
+                       abs_pos = winstate->frameheadpos + length - 1 + relpos;
+                       /* the frame head never moves back, so marking it is 
safe */
+                       mark_pos = winstate->frameheadpos;
                        break;
                default:
                        elog(ERROR, "unrecognized window seek type: %d", 
seektype);
-                       abs_pos = mark_pos = 0; /* keep compiler quiet */
                        break;
        }
 
        if (!window_gettupleslot(winobj, abs_pos, slot))
                goto out_of_frame;
 
-       /* The code above does not detect all out-of-frame cases, so check */
-       if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
-               goto out_of_frame;
+       /* matching never runs past the frame end, so the match lies within it 
*/
+       Assert(row_is_in_frame(winobj, abs_pos, slot, false) > 0);
 
        if (isout)
                *isout = false;
        if (set_mark)
-       {
-               /*
-                * If RPR is enabled and seek type is WINDOW_SEEK_TAIL, we set 
the
-                * mark position unconditionally to frameheadpos. In this case 
the
-                * frame always starts at CURRENT_ROW and never goes back, thus
-                * setting the mark at the position is safe.
-                */
-               if (winstate->rpPattern != NULL && seektype == WINDOW_SEEK_TAIL)
-                       mark_pos = winstate->frameheadpos;
                WinSetMarkPosition(winobj, mark_pos);
-       }
-       return 0;
+       econtext->ecxt_outertuple = slot;
+       return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
+                                               econtext, isnull);
 
 out_of_frame:
        if (isout)
                *isout = true;
        *isnull = true;
-       return -1;
+       return (Datum) 0;
 }
 
 
@@ -5494,6 +5283,8 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
        WindowAggState *winstate;
        ExprContext *econtext;
        TupleTableSlot *slot;
+       int64           abs_pos;
+       int64           mark_pos;
 
        Assert(WindowObjectIsValid(winobj));
        winstate = winobj->winstate;
@@ -5504,15 +5295,170 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
                return ignorenulls_getfuncarginframe(winobj, argno, relpos, 
seektype,
                                                                                
         set_mark, isnull, isout);
 
-       if (WinGetSlotInFrame(winobj, slot,
-                                                 relpos, seektype, set_mark,
-                                                 isnull, isout) == 0)
+       if (rpr_is_defined(winstate))
+               return WinGetFuncArgInReducedFrame(winobj, argno, relpos, 
seektype,
+                                                                               
   set_mark, isnull, isout);
+
+       switch (seektype)
        {
-               econtext->ecxt_outertuple = slot;
-               return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, 
argno),
-                                                       econtext, isnull);
+               case WINDOW_SEEK_CURRENT:
+                       elog(ERROR, "WINDOW_SEEK_CURRENT is not supported for 
WinGetFuncArgInFrame");
+                       abs_pos = mark_pos = 0; /* keep compiler quiet */
+                       break;
+               case WINDOW_SEEK_HEAD:
+                       /* rejecting relpos < 0 is easy and simplifies code 
below */
+                       if (relpos < 0)
+                               goto out_of_frame;
+                       update_frameheadpos(winstate);
+                       abs_pos = winstate->frameheadpos + relpos;
+                       mark_pos = abs_pos;
+
+                       /*
+                        * Account for exclusion option if one is active, but 
advance only
+                        * abs_pos not mark_pos.  This prevents changes of the 
current
+                        * row's peer group from resulting in trying to fetch a 
row before
+                        * some previous mark position.
+                        *
+                        * Note that in some corner cases such as current row 
being
+                        * outside frame, these calculations are theoretically 
too simple,
+                        * but it doesn't matter because we'll end up deciding 
the row is
+                        * out of frame.  We do not attempt to avoid fetching 
rows past
+                        * end of frame; that would happen in some cases anyway.
+                        */
+                       switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
+                       {
+                               case 0:
+                                       /* no adjustment needed */
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
+                                       if (abs_pos >= winstate->currentpos &&
+                                               winstate->currentpos >= 
winstate->frameheadpos)
+                                               abs_pos++;
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_GROUP:
+                                       update_grouptailpos(winstate);
+                                       if (abs_pos >= winstate->groupheadpos &&
+                                               winstate->grouptailpos > 
winstate->frameheadpos)
+                                       {
+                                               int64           overlapstart = 
Max(winstate->groupheadpos,
+                                                                               
                           winstate->frameheadpos);
+
+                                               abs_pos += 
winstate->grouptailpos - overlapstart;
+                                       }
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_TIES:
+                                       update_grouptailpos(winstate);
+                                       if (abs_pos >= winstate->groupheadpos &&
+                                               winstate->grouptailpos > 
winstate->frameheadpos)
+                                       {
+                                               int64           overlapstart = 
Max(winstate->groupheadpos,
+                                                                               
                           winstate->frameheadpos);
+
+                                               if (abs_pos == overlapstart)
+                                                       abs_pos = 
winstate->currentpos;
+                                               else
+                                                       abs_pos += 
winstate->grouptailpos - overlapstart - 1;
+                                       }
+                                       break;
+                               default:
+                                       elog(ERROR, "unrecognized frame option 
state: 0x%x",
+                                                winstate->frameOptions);
+                                       break;
+                       }
+                       break;
+               case WINDOW_SEEK_TAIL:
+                       /* rejecting relpos > 0 is easy and simplifies code 
below */
+                       if (relpos > 0)
+                               goto out_of_frame;
+                       update_frametailpos(winstate);
+                       abs_pos = winstate->frametailpos - 1 + relpos;
+
+                       /*
+                        * Account for exclusion option if one is active.  If 
there is no
+                        * exclusion, we can safely set the mark at the 
accessed row.  But
+                        * if there is, we can only mark the frame start, 
because we can't
+                        * be sure how far back in the frame the exclusion 
might cause us
+                        * to fetch in future.  Furthermore, we have to 
actually check
+                        * against frameheadpos here, since it's unsafe to try 
to fetch a
+                        * row before frame start if the mark might be there 
already.
+                        */
+                       switch (winstate->frameOptions & FRAMEOPTION_EXCLUSION)
+                       {
+                               case 0:
+                                       /* no adjustment needed */
+                                       mark_pos = abs_pos;
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_CURRENT_ROW:
+                                       if (abs_pos <= winstate->currentpos &&
+                                               winstate->currentpos < 
winstate->frametailpos)
+                                               abs_pos--;
+                                       update_frameheadpos(winstate);
+                                       if (abs_pos < winstate->frameheadpos)
+                                               goto out_of_frame;
+                                       mark_pos = winstate->frameheadpos;
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_GROUP:
+                                       update_grouptailpos(winstate);
+                                       if (abs_pos < winstate->grouptailpos &&
+                                               winstate->groupheadpos < 
winstate->frametailpos)
+                                       {
+                                               int64           overlapend = 
Min(winstate->grouptailpos,
+                                                                               
                         winstate->frametailpos);
+
+                                               abs_pos -= overlapend - 
winstate->groupheadpos;
+                                       }
+                                       update_frameheadpos(winstate);
+                                       if (abs_pos < winstate->frameheadpos)
+                                               goto out_of_frame;
+                                       mark_pos = winstate->frameheadpos;
+                                       break;
+                               case FRAMEOPTION_EXCLUDE_TIES:
+                                       update_grouptailpos(winstate);
+                                       if (abs_pos < winstate->grouptailpos &&
+                                               winstate->groupheadpos < 
winstate->frametailpos)
+                                       {
+                                               int64           overlapend = 
Min(winstate->grouptailpos,
+                                                                               
                         winstate->frametailpos);
+
+                                               if (abs_pos == overlapend - 1)
+                                                       abs_pos = 
winstate->currentpos;
+                                               else
+                                                       abs_pos -= overlapend - 
1 - winstate->groupheadpos;
+                                       }
+                                       update_frameheadpos(winstate);
+                                       if (abs_pos < winstate->frameheadpos)
+                                               goto out_of_frame;
+                                       mark_pos = winstate->frameheadpos;
+                                       break;
+                               default:
+                                       elog(ERROR, "unrecognized frame option 
state: 0x%x",
+                                                winstate->frameOptions);
+                                       mark_pos = 0;   /* keep compiler quiet 
*/
+                                       break;
+                       }
+                       break;
+               default:
+                       elog(ERROR, "unrecognized window seek type: %d", 
seektype);
+                       abs_pos = mark_pos = 0; /* keep compiler quiet */
+                       break;
        }
 
+       if (!window_gettupleslot(winobj, abs_pos, slot))
+               goto out_of_frame;
+
+       /* The code above does not detect all out-of-frame cases, so check */
+       if (row_is_in_frame(winobj, abs_pos, slot, false) <= 0)
+               goto out_of_frame;
+
+       if (isout)
+               *isout = false;
+       if (set_mark)
+               WinSetMarkPosition(winobj, mark_pos);
+       econtext->ecxt_outertuple = slot;
+       return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
+                                               econtext, isnull);
+
+out_of_frame:
        if (isout)
                *isout = true;
        *isnull = true;
-- 
2.34.1

Reply via email to