Hi all,

Resolved since the last post:

  D1. Single-row frame conformance, Subclause 6.10.2 -- Tatsuo's call [6]
      was to reject. Both ROWS BETWEEN CURRENT ROW AND CURRENT ROW and
      AND 0 FOLLOWING are now rejected (nocfbot-0025), which in turn
      unblocks the held ExecRPRProcessRow change (nocfbot-0026).

Open decisions (repeated with context at the end):

  D2. The RPRContext consolidation -- Tatsuo's call as co-author;
      non-blocking either way [4].
  D3. The AST "absorption" rename -- Tatsuo's call [2].

  [1] Jian's review, round 1 (2026-05-26):
      https://postgr.es/m/CACJufxH-DZePhbdJM=8nNYceQiSbbXXLTw54iLhxiynQ+4hbBA@mail.gmail.com
  [2] my round-1 reply -- AST "absorption" rename deferred to Tatsuo (D3, 2026-05-27):
      https://postgr.es/m/CAAAe_zDephfiDA_A3FN0hCymJRogEr=Rt3QoCTf4qMYDLk+xNA@mail.gmail.com
  [3] Jian's review, round 2 (2026-05-28):
      https://postgr.es/m/CACJufxGX17thWuEOq1tM5xbRHz2HXm1asooZC3GV25MYGmYqLQ@mail.gmail.com
  [4] my round-2 reply -- RPRContext deferred to Tatsuo (D2, 2026-05-29):
      https://postgr.es/m/CAAAe_zAH0MvP0TBmW3PTLeHjEpiyBz0473zJRM9pwLpseefMNw@mail.gmail.com
  [5] single-row frame conformance question (D1, 2026-05-29):
      https://postgr.es/m/CAAAe_zCbSU=dd-4qTL2QaBQwQ-cf51N_851a9Y5rOoz0wj0aXw@mail.gmail.com
  [6] Tatsuo's reply -- reject single-row frames (D1 resolved, 2026-05-30):
      https://postgr.es/m/20260530.114737.1416684464524168377.ishii@postgresql.org
  [7] Jian's review, round 3 (2026-05-30):
      https://postgr.es/m/CACJufxEsaU8GQ4yeXTWhAO8VjbrZTh5CpvUqz=4a3T0Cwz44pA@mail.gmail.com

Attached: the v47 feature series (v47-0001..0009) rebased onto current
master, plus the incremental patch series carried on top of it.

Base:

  9a41b34a287  2026-05-26  doc:  add comma to UPDATE docs, for consistency

Unchanged -- rebase only (already posted; only rebased, no content change).
Titles for reference:

  nocfbot-0001  Add DEFINE non-volatile baseline to rpr_integration B9
  nocfbot-0002  Unify RPR DEFINE walkers and reject volatile callees
  nocfbot-0003  Cover RPR empty-match path with EXPLAIN tests; fix stale XXX comments
  nocfbot-0004  Reclassify DEFINE qualifier check and reword diagnostic to "expression"
  nocfbot-0005  Sync stale comments on DEFINE/PATTERN handling
  nocfbot-0006  Add trailing commas to RPR enum definitions
  nocfbot-0007  Remove optional outer parentheses from ereport() calls in RPR files
  nocfbot-0008  Add high-water mark tracking to NFA visited bitmap reset
  nocfbot-0009  Document DEFINE subquery rejection as intentional over-rejection
  nocfbot-0010  Remove duplicate #include in nodeWindowAgg.c
  nocfbot-0011  Normalize SQL/RPR standard references
  nocfbot-0012  Add rpr_integration B7 cases for RPR in recursive query
  nocfbot-0013  Reject row pattern recognition in recursive queries
  nocfbot-0014  Enhance README.rpr per Tatsuo Ishii's review
  nocfbot-0015  Round out README.rpr WindowAggState field coverage
  nocfbot-0016  Add raw_expression_tree_walker coverage for RPR raw nodes

  (nocfbot-0016 was sent earlier as 0015; renumbered here so the review
  series runs contiguously from 0017.)

New incremental patches -- nocfbot-0017 onward. These apply Jian He's
review (rounds 1 [1] and 2 [3]) and settle D1. nocfbot-0017..0024 and
0026 are comment / doc / test plus a signature change and a couple of
Assert additions -- no behavior change. nocfbot-0025 is the one
user-visible change: it rejects the single-row frame per D1 [6].

  nocfbot-0017  Enhance README.rpr
      Chapter VIII absorption intro + a worked PATTERN (A+) trace;
      "Depth-First Search" spelled out at first use; the stale
      nfa_advance(initialAdvance=...) reference replaced.

  nocfbot-0018  Clarify execRPR.c comments and tighten an Assert
      Document the NFA invariants (compareDepth slot arithmetic, the
      asymmetric visited-marking scheme, the greedy/non-nullable BEGIN
      label, the ALT depth break, the state->next reset boundary),
      reframe ExecRPRFinalizeAllContexts as the partition-end policy
      holder, and add a defensive Assert in nfa_advance_var.

  nocfbot-0019  nfa_add_state_unique: bool return -> void
      The return value was unused.

  nocfbot-0020  Reluctant bounded mid-band test (rpr_nfa)
      A{3,5}? B, which drives the VAR-level count in nfa_advance_var
      through 3..5.

  nocfbot-0021  Define RPR absorption terminology in README.rpr
      Terminology block for the "match_start dep." column (none /
      direct / boundary check), the "boundary chk" typo fix, the
      count-dominance definition, and the match_start_dependent rename.

  nocfbot-0022  Document the get_reduced_frame_status cascade invariant
      The branches form an order-dependent early-return cascade; the
      running invariant is spelled out so the order reads as intentional.

  nocfbot-0023  Explain the completed-head-context branch in
                update_reduced_frame
      Why a head context can already be complete under SKIP TO NEXT ROW.

  nocfbot-0024  Tighten the frame-boundary check from >= to ==
      The > case is unreachable by the loop invariant; the defense moves
      into an Assert(currentPos <= ctxFrameEnd), and the comment changes
      from "exceeded" to "reached".

  nocfbot-0025  Reject single-row window frame in row pattern recognition
      Per D1 [6], the frame end must be UNBOUNDED FOLLOWING or a positive
      offset FOLLOWING. CURRENT ROW is rejected in transformRPR() at parse
      time; a zero offset -- which need not be a constant -- in
      calculate_frame_offsets() at run time. The two single-row rpr_base
      tests become error cases, with a bind-parameter error test added.

  nocfbot-0026  Remove the redundant zero check on the RPR frame ending
                offset
      With the single-row frame now rejected, a limited frame always
      carries a real offset, so the "endOffsetValue != 0" guard -- which
      compared a Datum directly to zero -- is dropped, leaving a plain
      DatumGetInt64(). This is the offset half of Jian's ExecRPRProcessRow
      cleanup; the structural half (dropping the hasLimitedFrame
      parameter) I've left as-is -- it is loop-invariant and computed once
      outside the per-row loop, so moving it into ExecRPRProcessRow would
      just repeat the work each row.

Coverage -- my decision summaries [2], [4], with the patch each became:

  Round 1 [2]
    Short-circuit optimization       Separate series   -> separate series
    Absorption README narrative      Accept            -> nocfbot-0017
    AST-level "absorption" rename    Pending Tatsuo    -> D3
    DFS expansion                    Accept            -> nocfbot-0017
    initialAdvance README mismatch   Accept            -> nocfbot-0017
    Defensive Assert in advance_var  Accept            -> nocfbot-0018
    Finalize unnecessary?            Keep              -> nocfbot-0018
    Greedy comment label             Accept            -> nocfbot-0018
    state->next reset                Decline           -> nocfbot-0018
    count >= 3 test coverage         Accept            -> nocfbot-0020
    visited marking purpose          Accept            -> nocfbot-0018
    compareDepth comment             Accept            -> nocfbot-0018
    Unused bool return               Accept            -> nocfbot-0019
    ALT depth invariant Assert       Decline           -> nocfbot-0018

  Round 2 [4]
    RPRContext consolidation         Tatsuo's call     -> D2
    get_reduced_frame_status order   Keep              -> nocfbot-0022
    README terminology + typo        Accept            -> nocfbot-0021
    ExecRPRProcessRow refactor       Datum fix only    -> nocfbot-0026
    single-row frame (6.10.2, D1)    Reject (Tatsuo)   -> nocfbot-0025
    currentPos >= -> ==              Accept            -> nocfbot-0024
    states == NULL branch coverage   Already reached   -> nocfbot-0023

Remaining work and decisions

  Decisions (need your input):
    D2  RPRContext consolidation -- Tatsuo.
    D3  AST "absorption" rename -- Tatsuo.

  Held pending a decision:
    - the RPRContext consolidation itself -- done if D2 says go.
    - the AST "absorption" rename itself -- done if D3 says go.

  (D1 settled: the parse-time frame-end check, the offset-0 test as an
   error case, and the Datum-comparison fix are now in nocfbot-0025/0026.)

  From Jian's round-3 review [7] (into the next revision):
    - four comment fixes: the line-number test anchors, the "at the END"
      and uppercase-END wording, and the nfa_advance_var count premise.
    - the make/clone rename of the NFA helpers (one of three attached
      refactors); the other two -- dropping nfaStateSize and the elem
      parameter -- I'd keep, both on hot-path grounds.

  From our in-house review (separate follow-ups):
    - a quantifier-normalization correctness fix (nested unbounded
      quantifiers such as (A{2,})* are currently mis-normalized)
    - a per-tuple memory-context fix in DEFINE evaluation (still verifying)
    - smaller correctness/conformance fixes (an overflow guard, a few
      missing parse-time checks, EXPLAIN output details)
    - documentation gaps (comments, README, SGML)
    - added regression tests (round-trip deparse, edge-case offsets)

  Before the v48 fold (from Jian's off-list comments):
    - INT_MAX -> PG_INT32_MAX (the unbounded-quantifier sentinel; ~24 sites)
    - foreach + lfirst() -> foreach_node (~33 sites)
    - foreach_current_index, dropping the redundant break (3 sites)

  Work, no decision needed:
    - short-circuit (lazy eval) -- stop evaluating a DEFINE predicate
      once its outcome is fixed. A separate series.
      It turns on a standard-interpretation point -- whether skipping is
      sound when a dropped subexpression has side effects.

Thanks again to Jian for the careful reading.

Henson