public inbox for [email protected]
help / color / mirror / Atom feedFrom: Henson Choi <[email protected]>
To: jian he <[email protected]>
Cc: Tatsuo Ishii <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Subject: Re: Row pattern recognition
Date: Fri, 29 May 2026 10:28:29 +0900
Message-ID: <CAAAe_zAH0MvP0TBmW3PTLeHjEpiyBz0473zJRM9pwLpseefMNw@mail.gmail.com> (raw)
In-Reply-To: <CACJufxGX17thWuEOq1tM5xbRHz2HXm1asooZC3GV25MYGmYqLQ@mail.gmail.com>
References: <[email protected]>
<[email protected]>
<CACJufxEWL_ZnC-bs_yrg-Ys6ZUD3Ut_p1Ebj0bAcbzj67+HDAQ@mail.gmail.com>
<[email protected]>
<CACJufxH-DZePhbdJM=8nNYceQiSbbXXLTw54iLhxiynQ+4hbBA@mail.gmail.com>
<CAAAe_zDephfiDA_A3FN0hCymJRogEr=Rt3QoCTf4qMYDLk+xNA@mail.gmail.com>
<CACJufxGX17thWuEOq1tM5xbRHz2HXm1asooZC3GV25MYGmYqLQ@mail.gmail.com>
Hi Jian,
Thanks for the careful read of execRPR.c and the NFA -- several of these
landed on real gaps in our comments/docs. Going through your points
inline below, with a summary of decisions at the end.
> In src/include/nodes/execnodes.h, we're adding quite a few fields to
> WindowAggState that are only used for RPR queries.
> Should we consolidate these fields behind a single pointer (named
> RPRContext) to keep the WindowAggState size smaller for non-RPR
> queries?
The size win is real -- it's roughly 450-500 bytes per WindowAggState
that every non-RPR window query carries today. But it takes a wide
code change to get there: it reshapes every RPR access path, a
sizable (if mostly mechanical) diff across nodeWindowAgg.c,
execRPR.c, and the explain side.
Tatsuo, as co-author -- do you want this in v48? If you do, I'll
prepare the RPRContext consolidation as an incremental patch for you to
fold into v48. It isn't blocking either way.
> In function get_reduced_frame_status, I made the following changes:
> [conditionA..D probe elided]
> I re-ran the regression tests and noticed it's entirely possible for
> multiple conditions to be true at once. Because of this, all the IF
> blocks in get_reduced_frame_status (after the initial one) are
> order-dependent. If I move one IF statement above another,
> get_reduced_frame_status may return a different result.
> Is this the expected behavior? It seems like a potential logic flaw.
You're right that the conditions aren't mutually exclusive and that
reordering the branches changes the result -- but that's the
cascade-with-early-return idiom, standard in C and used throughout the
backend, not a logic flaw. Each branch is the minimal test given the
negations the earlier returns have already established.
heapam_visibility.c is the canonical example: the HeapTupleSatisfies*
family cascades through ordered early returns, with the later XMAX
branches correct only because the XMIN-committed invariant above holds.
The order is correct, not just idiomatic: update_reduced_frame() only
ever records (matched, length) as (false, 1), (true, 0), or (true, >=1),
and A->B->C->D is the order that classifies exactly those three cases --
reorder it and one of them gets misclassified.
I'll add short "by here, ..." comments between the branches to make the
invariants visible, but keep the structure. If you feel strongly it
should be restructured, I'd defer to Tatsuo on that.
> "boundary chk" should be "boundary check".
> column "match_start dep.", I don't understand the meaning of "direct"
> and "boundary check".
> "count-dominance" seems like an invented word, but I could not find
> the explanation.
> "match_start-dependent" occurred many times, it would also deserve an
> explanation. We can also rename it as match_start_dependent.
All fair. v48 fixes the typo and adds a terminology block to README.rpr
defining count-dominance (the VIII-3 cover condition, named there),
match_start_dependent (renamed to the underscore spelling, to match the
defineMatchStartDependent identifier), and the "direct" vs "boundary
check" navigation distinction.
> Attached is a minor refactoring of ExecRPRProcessRow, no need for
> boolean hasLimitedFrame.
> ``if (hasLimitedFrame && winstate->endOffsetValue != 0)`` seems wrong
> to me, endOffsetValue is a Datum, and you directly compare it with 0.
> see also calculate_frame_offsets.
Good eye on the Datum comparison, and I agree your version reads more
clearly -- gating on the frame-option flag and going through
DatumGetInt64() rather than comparing a Datum to 0, plus the two new
Asserts. (The existing code is correct, since hasLimitedFrame carries
the limited/unbounded distinction separately, but the refactor is the
clearer shape.) I'll take it.
One thing to watch when dropping hasLimitedFrame, though: a zero offset
is still a valid limited frame -- ROWS BETWEEN CURRENT ROW AND CURRENT
ROW, and ... AND 0 FOLLOWING -- so the new check must not treat offset 0
as unbounded, otherwise the boundary check is skipped and a quantified
pattern (A+) silently absorbs the whole partition. The limited/unbounded
distinction lives in the frame-option flag, not the offset value, so the
refactor has to keep that flag in the picture.
We don't currently cover the offset-0 quantified case -- the two
zero-offset frame tests in rpr_base both use a plain PATTERN (A) -- so
I'll add an A+ regression test for it.
> [nfa_evaluate_row's window_gettupleslot returning false]
> indicates that it's not possible for (currentPos > ctxFrameEnd).
> therefore [if (currentPos >= ctxFrameEnd) ...]
> should change to ``if (currentPos == ctxFrameEnd)``.
> With that, the comment wording "exceeded" seems wrong too.
Agreed. `>` is unreachable by the loop invariant -- currentPos advances
by exactly one per row, and once a context is finalized the
states == NULL guard skips it -- so >= and == behave identically here,
and == states the intent better. The `>=` was a defensive guard against
an overshoot that can't actually happen; v48 tightens it to ==, changes
the comment from "exceeded" to "reached", and moves that defense into an
Assert(currentPos <= ctxFrameEnd) so a future change that breaks the
invariant trips immediately instead of silently.
> Since I couldn't generate a coverage report, I am using elog(INFO) to
> test reachability. Rerun the regress tests, you will find out that the
> ELSE IF branch below is not reached by current regress tests.
> else if (targetCtx->states == NULL)
> {
> /* Context already completed - skip to result registration */
> goto register_result;
> elog(INFO, "XXXX reached");
> }
The elog in your snippet sits *after* the goto, so it can never print
even though the branch runs. Moving it above the goto shows it firing
across the suite. Easy mistake; I've put a probe right after a
goto/return more times than I'd like to admit.
The branch is correct as written: under SKIP TO NEXT ROW an overlapping
context can finish (and be preserved by cleanup, since
matchEndRow >= matchStartRow) before the next update_reduced_frame()
call lands on its start row. So no new test is needed -- I'll just add a
comment above it explaining why the head context can already be complete
there.
Summary of decisions, in the order above:
RPRContext consolidation Tatsuo's call -- non-blocking for v48
get_reduced_frame_status order Keep -- cascade idiom; add comments
README terminology + typo Accept -- definitions block + rename
ExecRPRProcessRow refactor Accept -- but fix frameOffset
currentPos >= -> == Accept -- plus comment + Assert
states == NULL branch coverage Already reached (155x) -- add comment
I'll post an incremental patch shortly with the accepted changes and the
comment additions; the RPRContext question is for Tatsuo.
Thanks again,
Henson
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: Row pattern recognition
In-Reply-To: <CAAAe_zAH0MvP0TBmW3PTLeHjEpiyBz0473zJRM9pwLpseefMNw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox