Hi Tatsuo,
Thank you for the careful review and excellent questions.
I was not able to find the definition for EEOP_GT. Is this a new one?
>
You're right -- EEOP_GT does not exist. I oversimplified the example.
In PostgreSQL, comparison operators like `>` are not dedicated opcodes.
They are dispatched through EEOP_FUNCEXPR variants as regular function
calls. For example, `price > PREV(price)`:
- The parser resolves `>` to the appropriate pg_operator entry
- The expression compiler emits an EEOP_FUNCEXPR variant
- At runtime, op->d.func.fn_addr points to the operator function,
I am afraid ExecEvalExpr does not allow this because ExecEvalExpr does
> not accept arguments like WindowObject or WindowAggState that are
> necessary for accessing the tuplestore created for the Window
> aggregate node.
This is the key question. You are right that ExecEvalExpr's signature
does not accept WindowAggState, and I do not intend to change it.
The approach is to pass WindowAggState through the step's own payload,
the same way EEOP_WINDOW_FUNC carries WindowFuncExprState in
op->d.window_func.wfstate. Each ExprEvalStep has a union of per-opcode
payload structs. A new payload for RPR navigation would look like:
struct
{
WindowAggState *winstate; /* access to tuplestore */
int64 offset; /* signed offset: PREV=-1, NEXT=+1 */
} rpr_nav;
The expression compiler (ExecInitExprRec) populates this when it
encounters a PREV/NEXT node, storing the pointer to the enclosing
WindowAggState. At runtime, EEOP_RPR_NAV_SET retrieves it from the
payload and uses it to fetch the target row:
/* pseudo-code for EEOP_RPR_NAV_SET */
winstate = op->d.rpr_nav.winstate;
/* 1. save current slot into winstate for later restore */
winstate->saved_outertuple = econtext->ecxt_outertuple;
/* 2. compute target position */
target_pos = winstate->currentpos + op->d.rpr_nav.offset;
/* 3. fetch target row from tuplestore via winstate */
target_slot = fetch_row_from_tuplestore(winstate, target_pos);
/* 4. swap */
econtext->ecxt_outertuple = target_slot;
/* pseudo-code for EEOP_RPR_NAV_RESTORE */
winstate = op->d.rpr_nav.winstate;
econtext->ecxt_outertuple = winstate->saved_outertuple;
The key difference from EEOP_WINDOW_FUNC: existing steps read
precomputed values, but PREV/NEXT requires fetching a *different*
row at runtime -- because the target depends on the current row
position during pattern matching, which is not known in advance.
The mechanism for passing state is the same (step payload), but
the runtime behavior is a departure from the existing pattern.
I have examined the alternatives carefully:
- The precomputation approach (EEOP_WINDOW_FUNC style) does not apply
here: there is no fixed set of values to precompute, because the
target row depends on the current position during pattern matching.
- The three-slot model (current implementation) cannot support variable
offsets like PREV(price, 3), and requires varno rewriting that adds
complexity elsewhere.
- A callback-based approach (registering a fetch function in econtext)
would still require passing WindowAggState somehow, just through a
different indirection.
The step payload approach is the only path I have found that:
- Leaves ExecEvalExpr's signature unchanged
- Eliminates varno rewriting entirely
- Supports variable offsets naturally
- Extends to FIRST/LAST navigation later
If anyone sees a cleaner path, I would genuinely welcome the
discussion.
I plan to have an experimental implementation ready before next week.
The initial version will pass WindowAggState broadly; we can
narrow the interface to the minimum required during review.
Let's continue the discussion with actual code.
Best regards,
Henson