From 3a3a9c89fa01496d91290eac16499047e7db1415 Mon Sep 17 00:00:00 2001 From: jian he Date: Thu, 2 Jul 2026 17:23:51 +0800 Subject: [PATCH v50 1/2] Evaluate navigation offset once at one place Row pattern navigation (PREV/NEXT/FIRST/LAST and their compounds) resolved offsets (transform a expression to a int64 number) in ExecInitExprRec, ExecEvalRPRNavSet, extract_const_offset, eval_define_offsets. Serval places transform the same offset expression to a int64 number, that is too much! With the attached, a single place (ExecInitWindowAgg->eval_define_offsets) to handling this. visit_nav_exec() now evaluates every offset expression at executor init and save the result on the RPRNavExpr (offset / compound_offset); ExecEvalRPRNavSet() simply reads those int64 values. This removes: - the RPRNavOffsetKind enum and the offset/kind fields on the WindowAgg plan node. The planner walk (visit_nav_plan / compute_define_metadata) now only classifies match_start dependency, which is all buildRPRPattern() needs to decide context absorption. - the per-invocation offset machinery in the executor: the Datum* offset arrays in the eval step, rpr_nav_get_compound_offset(), and the scattered NULL/negative checks, now centralized in eval_nav_offset. The "retain all" case (backward reach overflows int64) is encoded as the sentinel navMaxOffset < 0 rather than an enum value; advance_nav_mark() disables tuplestore trim on it. EXPLAIN reads the resolved offsets from the planstate (ExecInitWindowAgg is reached for EXPLAIN without ANALYZE), so it now prints concrete offsets instead of "runtime". ``` We must still set nav_saved_outertuple (done above) so that EEOP_RPR_NAV_RESTORE is a harmless no-op. ``` The above in ExecEvalRPRNavSet is wrong, EEOP_RPR_NAV_RESTORE will never be a no-op. However we can make EEOP_RPR_NAV_RESTORE no-op, so the above comments make sense. Rename eval_nav_offset_helper to eval_nav_offset Based on https://github.com/assam258-5892/postgres/commits/RPR --- src/backend/commands/explain.c | 69 +--- src/backend/executor/execExpr.c | 56 +--- src/backend/executor/execExprInterp.c | 80 +---- src/backend/executor/nodeWindowAgg.c | 203 ++++++------ src/backend/optimizer/plan/createplan.c | 299 ++---------------- src/include/executor/execExpr.h | 6 +- src/include/nodes/execnodes.h | 10 +- src/include/nodes/parsenodes.h | 15 - src/include/nodes/plannodes.h | 21 -- src/include/nodes/primnodes.h | 41 ++- src/test/regress/expected/rpr_explain.out | 6 +- src/test/regress/expected/rpr_integration.out | 2 +- src/tools/pgindent/typedefs.list | 1 - 13 files changed, 188 insertions(+), 621 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a5d272b632..c7c2d71bc7 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3212,11 +3212,6 @@ show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es) /* Show Row Pattern Recognition pattern if present */ if (wagg->rpPattern != NULL) { - RPRNavOffsetKind maxKind = wagg->navMaxOffsetKind; - int64 maxOffset = wagg->navMaxOffset; - RPRNavOffsetKind firstKind = wagg->navFirstOffsetKind; - int64 firstOffset = wagg->navFirstOffset; - char *patternStr = deparse_rpr_pattern(wagg->rpPattern); ExplainPropertyText("Pattern", patternStr, es); @@ -3224,58 +3219,24 @@ show_window_def(WindowAggState *planstate, List *ancestors, ExplainState *es) pfree(patternStr); /* - * Show navigation offsets for tuplestore trim. For EXPLAIN ANALYZE, - * use the executor-resolved values (which may differ from the plan - * when NEEDS_EVAL was resolved to FIXED or RETAIN_ALL at init). + * Navigation offsets for tuplestore trim are resolved at executor + * init, which runs even for plain EXPLAIN, so read the resolved + * values from the planstate. navMaxOffset < 0 is the retain-all + * sentinel (trim disabled). */ - if (es->analyze) - { - maxKind = planstate->navMaxOffsetKind; - maxOffset = planstate->navMaxOffset; - firstKind = planstate->navFirstOffsetKind; - firstOffset = planstate->navFirstOffset; - } - - switch (maxKind) - { - case RPR_NAV_OFFSET_NEEDS_EVAL: - ExplainPropertyText("Nav Mark Lookback", "runtime", es); - break; - case RPR_NAV_OFFSET_RETAIN_ALL: - ExplainPropertyText("Nav Mark Lookback", "retain all", es); - break; - case RPR_NAV_OFFSET_FIXED: - ExplainPropertyInteger("Nav Mark Lookback", NULL, - maxOffset, es); - break; - default: - elog(ERROR, "unrecognized RPR nav offset kind: %d", - maxKind); - break; - } + if (planstate->navMaxOffset < 0) + ExplainPropertyText("Nav Mark Lookback", "retain all", es); + else + ExplainPropertyInteger("Nav Mark Lookback", NULL, + planstate->navMaxOffset, es); - if (wagg->hasFirstNav) + if (planstate->hasFirstNav) { - switch (firstKind) - { - case RPR_NAV_OFFSET_NEEDS_EVAL: - ExplainPropertyText("Nav Mark Lookahead", "runtime", - es); - break; - case RPR_NAV_OFFSET_FIXED: - if (firstOffset == PG_INT64_MAX) - ExplainPropertyText("Nav Mark Lookahead", "infinite", - es); - else - ExplainPropertyInteger("Nav Mark Lookahead", NULL, - firstOffset, es); - break; - default: - /* RPR_NAV_OFFSET_RETAIN_ALL is lookback-only, never here */ - elog(ERROR, "unrecognized RPR nav offset kind: %d", - firstKind); - break; - } + if (planstate->navFirstOffset == PG_INT64_MAX) + ExplainPropertyText("Nav Mark Lookahead", "infinite", es); + else + ExplainPropertyInteger("Nav Mark Lookahead", NULL, + planstate->navFirstOffset, es); } } } diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 3036782503..de13ac693f 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -1198,10 +1198,6 @@ ExecInitExprRec(Expr *node, ExprState *state, * swaps ecxt_outertuple to the target row, the argument * expression is compiled normally (reads from the swapped * slot), and the RESTORE opcode restores the original slot. - * - * Default offset when offset_arg is NULL: PREV/NEXT: 1 - * (physical offset from currentpos) FIRST/LAST: 0 (logical - * offset from match boundary) */ RPRNavExpr *nav = (RPRNavExpr *) node; WindowAggState *winstate; @@ -1213,56 +1209,8 @@ ExecInitExprRec(Expr *node, ExprState *state, scratch.opcode = EEOP_RPR_NAV_SET; scratch.d.rpr_nav.winstate = winstate; scratch.d.rpr_nav.kind = nav->kind; - - if (nav->kind >= RPR_NAV_PREV_FIRST) - { - /* - * Compound navigation: allocate array of 2 for inner [0] - * and outer [1] offsets. - */ - Datum *offset_values = palloc_array(Datum, 2); - bool *offset_isnulls = palloc_array(bool, 2); - - /* Inner offset (default 0 for FIRST/LAST) */ - if (nav->offset_arg != NULL) - ExecInitExprRec(nav->offset_arg, state, - &offset_values[0], &offset_isnulls[0]); - else - { - offset_values[0] = Int64GetDatum(0); - offset_isnulls[0] = false; - } - - /* Outer offset (default 1 for PREV/NEXT) */ - if (nav->compound_offset_arg != NULL) - ExecInitExprRec(nav->compound_offset_arg, state, - &offset_values[1], &offset_isnulls[1]); - else - { - offset_values[1] = Int64GetDatum(1); - offset_isnulls[1] = false; - } - - scratch.d.rpr_nav.offset_value = offset_values; - scratch.d.rpr_nav.offset_isnull = offset_isnulls; - } - else if (nav->offset_arg != NULL) - { - /* Simple navigation with explicit offset */ - Datum *offset_value = palloc_object(Datum); - bool *offset_isnull = palloc_object(bool); - - ExecInitExprRec(nav->offset_arg, state, - offset_value, offset_isnull); - scratch.d.rpr_nav.offset_value = offset_value; - scratch.d.rpr_nav.offset_isnull = offset_isnull; - } - else - { - /* Simple navigation with default offset */ - scratch.d.rpr_nav.offset_value = NULL; - scratch.d.rpr_nav.offset_isnull = NULL; - } + scratch.d.rpr_nav.offset = nav->offset; + scratch.d.rpr_nav.compound_offset = nav->compound_offset; ExprEvalPushStep(state, &scratch); diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 5b6281e9e3..33960b757d 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -6011,34 +6011,6 @@ ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans, MemoryContextSwitchTo(oldContext); } -/* - * Extract compound (outer) offset from step data. - * For compound nav, offset_value is an array: [0]=inner, [1]=outer. - * Returns the outer offset; errors on NULL or negative. - * Default is 1 (like PREV/NEXT implicit offset). - */ -static int64 -rpr_nav_get_compound_offset(ExprEvalStep *op) -{ - int64 val; - - Assert(op->d.rpr_nav.offset_value != NULL); - - if (op->d.rpr_nav.offset_isnull[1]) - ereport(ERROR, - errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("row pattern navigation offset must not be null")); - - val = DatumGetInt64(op->d.rpr_nav.offset_value[1]); - - if (val < 0) - ereport(ERROR, - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("row pattern navigation offset must not be negative")); - - return val; -} - /* * Evaluate RPR navigation (PREV/NEXT/FIRST/LAST): swap slot to target row. * @@ -6054,42 +6026,15 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) { WindowAggState *winstate = op->d.rpr_nav.winstate; int64 offset; + int64 compound_offset; int64 target_pos; TupleTableSlot *target_slot; /* Save current slot for later restore */ winstate->nav_saved_outertuple = econtext->ecxt_outertuple; - /* - * Determine the inner offset. NULL or negative offsets are errors per - * the SQL standard. - * - * Default offset when offset_arg is NULL: PREV/NEXT: 1 (standard 5.6.2) - * FIRST/LAST and compound: 0 for inner, 1 for outer - */ - if (op->d.rpr_nav.offset_value != NULL) - { - if (*op->d.rpr_nav.offset_isnull) - ereport(ERROR, - errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("row pattern navigation offset must not be null")); - - offset = DatumGetInt64(*op->d.rpr_nav.offset_value); - - if (offset < 0) - ereport(ERROR, - errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("row pattern navigation offset must not be negative")); - } - else - { - /* Default offset: 1 for simple PREV/NEXT, 0 otherwise */ - if (op->d.rpr_nav.kind == RPR_NAV_PREV || - op->d.rpr_nav.kind == RPR_NAV_NEXT) - offset = 1; - else - offset = 0; - } + offset = op->d.rpr_nav.offset; + compound_offset = op->d.rpr_nav.compound_offset; /* * Calculate target position based on navigation direction. On overflow, @@ -6129,7 +6074,6 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) case RPR_NAV_PREV_FIRST: case RPR_NAV_NEXT_FIRST: { - int64 compound_offset; int64 inner_pos; /* Inner: match_start + offset */ @@ -6144,9 +6088,6 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) break; } - /* Outer offset */ - compound_offset = rpr_nav_get_compound_offset(op); - /* Apply outer: PREV subtracts, NEXT adds */ if (op->d.rpr_nav.kind == RPR_NAV_PREV_FIRST) { @@ -6168,7 +6109,6 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) case RPR_NAV_PREV_LAST: case RPR_NAV_NEXT_LAST: { - int64 compound_offset; int64 inner_pos; /* Inner: currentpos - offset */ @@ -6183,9 +6123,6 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) break; } - /* Outer offset */ - compound_offset = rpr_nav_get_compound_offset(op); - /* Apply outer: PREV subtracts, NEXT adds */ if (op->d.rpr_nav.kind == RPR_NAV_PREV_LAST) { @@ -6206,7 +6143,7 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) break; default: elog(ERROR, "unrecognized RPR navigation kind: %d", - op->d.rpr_nav.kind); + (int) op->d.rpr_nav.kind); break; } @@ -6238,8 +6175,6 @@ ExecEvalRPRNavSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext) * Evaluate RPR navigation: restore slot to original row. * * Restores econtext->ecxt_outertuple from the saved slot in winstate. - * When slot swap was elided (target == currentpos), this is a harmless - * no-op since saved and current slots are identical. * The caller is responsible for updating any local slot cache. * * For pass-by-reference result types, the result datum points into @@ -6255,6 +6190,13 @@ ExecEvalRPRNavRestore(ExprState *state, ExprEvalStep *op, { WindowAggState *winstate = op->d.rpr_nav.winstate; + /* + * When slot swap was elided (target == currentpos), this is a harmless + * no-op since saved and current slots are identical. + */ + if (econtext->ecxt_outertuple == winstate->nav_saved_outertuple) + return; + econtext->ecxt_outertuple = winstate->nav_saved_outertuple; /* Stabilize pass-by-ref result against nav_slot re-fetch */ diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index e4e97a6ed9..caa27ffe8e 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1261,13 +1261,13 @@ prepare_tuplestore(WindowAggState *winstate) /* * Allocate mark and read pointers for RPR navigation. * - * If navMaxOffsetKind == RPR_NAV_OFFSET_FIXED, we advance the mark - * based on (currentpos - navMaxOffset) and optionally + * If navMaxOffset is non-negative, we advance the mark based on + * (currentpos - navMaxOffset) and optionally * (nfaContext->matchStartRow + navFirstOffset), allowing * tuplestore_trim() to free rows that are no longer reachable. * - * RPR_NAV_OFFSET_NEEDS_EVAL is resolved at executor init; by this - * point it is either FIXED or RETAIN_ALL. + * the offset is resolved at executor init; by this point it is either + * FIXED or RETAIN_ALL. */ winstate->nav_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0); @@ -3037,12 +3037,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) winstate->rpSkipTo = node->rpSkipTo; /* Set up row pattern recognition PATTERN clause (compiled NFA) */ winstate->rpPattern = node->rpPattern; - /* Set up nav offsets for tuplestore trim; resolve any NEEDS_EVAL kinds */ - winstate->navMaxOffsetKind = node->navMaxOffsetKind; - winstate->navMaxOffset = node->navMaxOffset; - winstate->hasFirstNav = node->hasFirstNav; - winstate->navFirstOffsetKind = node->navFirstOffsetKind; - winstate->navFirstOffset = node->navFirstOffset; + /* Resolve nav offsets for tuplestore trim from the DEFINE clause */ eval_define_offsets(winstate, node->defineClause); /* Copy match_start dependency bitmapset for per-context evaluation */ @@ -3983,20 +3978,16 @@ put_notnull_info(WindowObject winobj, int64 pos, int argno, bool isnull) } /* - * eval_nav_offset_helper - * Pre-evaluate a navigation offset expression at executor init time, to - * bound how far navigation can reach (which sizes the frame trim). - * Returns the offset value, or 0 for a NULL or negative offset. + * eval_nav_offset + * Evaluate a row pattern navigation offset expression * - * The offset is not validated here. A NULL or negative value is caught later, - * per row, on the navigation path that consumes it (see EEOP_RPR_NAV_SET in - * execExprInterp.c), which errors out before navigation produces any result; - * the trim sizing computed from such an offset is therefore never used, and 0 - * is returned as a harmless placeholder. + * The resolved value bounds how far navigation can reach, which in turn + * sizes the tuplestore trim. offset_expr may be either a simple offset or + * the outer (compound) offset of a compound navigation. Returns the offset + * as an int64; a NULL or negative result is an error per the SQL standard. */ static int64 -eval_nav_offset_helper(WindowAggState *winstate, Expr *offset_expr, - int64 defaultOffset) +eval_nav_offset(WindowAggState *winstate, Expr *offset_expr) { ExprContext *econtext = winstate->ss.ps.ps_ExprContext; ExprState *estate; @@ -4004,18 +3995,20 @@ eval_nav_offset_helper(WindowAggState *winstate, Expr *offset_expr, bool isnull; int64 offset; - if (offset_expr == NULL) - return defaultOffset; - estate = ExecInitExpr(offset_expr, (PlanState *) winstate); val = ExecEvalExprSwitchContext(estate, econtext, &isnull); if (isnull) - return 0; + ereport(ERROR, + errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("row pattern navigation offset must not be null")); offset = DatumGetInt64(val); + if (offset < 0) - return 0; + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("row pattern navigation offset must not be negative")); return offset; } @@ -4028,13 +4021,14 @@ typedef struct bool maxOverflow; /* true if backward-reach overflow detected */ int64 minFirstOffset; /* min forward-from-match_start offset; may be * negative (PREV_FIRST: inner - outer < 0) */ + bool hasFirst; /* any FIRST-based nav found */ } EvalDefineOffsetsContext; /* * visit_nav_exec * nav_traversal_walker callback (NavVisitFn) for the executor side. * At each RPRNavExpr, evaluates the nav's offset expression(s) at - * runtime via eval_nav_offset_helper and accumulates: + * runtime via eval_nav_offset and accumulates: * * - maxOffset (backward reach): PREV, LAST-with-offset, compound * PREV_LAST (sets maxOverflow on int64 overflow), compound @@ -4052,6 +4046,9 @@ typedef struct static void visit_nav_exec(NavTraversal *t, RPRNavExpr *nav) { + int64 inner; + int64 outer; + EvalDefineOffsetsContext *context = (EvalDefineOffsetsContext *) t->data; /* @@ -4064,69 +4061,61 @@ visit_nav_exec(NavTraversal *t, RPRNavExpr *nav) Assert(nav->compound_offset_arg == NULL || !IsA(nav->compound_offset_arg, RPRNavExpr)); + if (nav->offset_arg != NULL) + inner = eval_nav_offset(context->winstate, + nav->offset_arg); + else if (nav->kind == RPR_NAV_PREV || nav->kind == RPR_NAV_NEXT) + inner = 1; + else + inner = 0; + + if (nav->compound_offset_arg != NULL) + outer = eval_nav_offset(context->winstate, + nav->compound_offset_arg); + else + outer = 1; + + nav->offset = inner; + nav->compound_offset = outer; + /* Backward reach: PREV, LAST-with-offset */ if (!context->maxOverflow) { - int64 reach = 0; - bool gotReach = false; - - if (nav->kind == RPR_NAV_PREV) + if (nav->kind == RPR_NAV_PREV || + nav->kind == RPR_NAV_LAST || + nav->kind == RPR_NAV_PREV_LAST || + nav->kind == RPR_NAV_NEXT_LAST) { - reach = eval_nav_offset_helper(context->winstate, - nav->offset_arg, 1); - gotReach = true; - } - else if (nav->kind == RPR_NAV_LAST && nav->offset_arg != NULL) - { - reach = eval_nav_offset_helper(context->winstate, - nav->offset_arg, 0); - gotReach = true; - } - else if (nav->kind == RPR_NAV_PREV_LAST || - nav->kind == RPR_NAV_NEXT_LAST) - { - int64 inner = eval_nav_offset_helper(context->winstate, - nav->offset_arg, 0); - int64 outer = eval_nav_offset_helper(context->winstate, - nav->compound_offset_arg, 1); + int64 reach = 0; - if (nav->kind == RPR_NAV_PREV_LAST) + if (nav->kind == RPR_NAV_PREV || nav->kind == RPR_NAV_LAST) + reach = inner; + else if (nav->kind == RPR_NAV_PREV_LAST) { if (pg_add_s64_overflow(inner, outer, &reach)) context->maxOverflow = true; - else - gotReach = true; } else - { reach = Max(inner - outer, 0); - gotReach = true; - } - } - if (gotReach) - context->maxOffset = Max(context->maxOffset, reach); + if (!context->maxOverflow) + context->maxOffset = Max(context->maxOffset, reach); + } } /* Forward reach from match_start: FIRST, compound PREV_FIRST/NEXT_FIRST */ - if (nav->kind == RPR_NAV_FIRST) + if (nav->kind == RPR_NAV_FIRST || + nav->kind == RPR_NAV_PREV_FIRST || + nav->kind == RPR_NAV_NEXT_FIRST) { - int64 reach; + int64 reach = 0; - reach = eval_nav_offset_helper(context->winstate, - nav->offset_arg, 0); - context->minFirstOffset = Min(context->minFirstOffset, reach); - } - else if (nav->kind == RPR_NAV_PREV_FIRST || - nav->kind == RPR_NAV_NEXT_FIRST) - { - int64 inner = eval_nav_offset_helper(context->winstate, - nav->offset_arg, 0); - int64 outer = eval_nav_offset_helper(context->winstate, - nav->compound_offset_arg, 1); - int64 reach; + context->hasFirst = true; - if (nav->kind == RPR_NAV_PREV_FIRST) + if (nav->kind == RPR_NAV_FIRST) + context->minFirstOffset = Min(context->minFirstOffset, + inner); + else if (nav->kind == RPR_NAV_PREV_FIRST) { /* * reach = inner - outer. Both are non-negative, so the result >= @@ -4150,33 +4139,38 @@ visit_nav_exec(NavTraversal *t, RPRNavExpr *nav) /* * eval_define_offsets - * Evaluate non-constant nav offsets at executor init time. + * Compute navigation offsets for tuplestore trim from the DEFINE clause, + * at executor init. * - * Called when the planner set navMaxOffsetKind and/or navFirstOffsetKind - * to RPR_NAV_OFFSET_NEEDS_EVAL because some offset contains a parameter - * or non-foldable expression. Updates only the fields whose kind was - * NEEDS_EVAL; FIXED kinds are left unchanged. + * Evaluates every nav offset expression (constant or not) via visit_nav_exec + * and stores the results in the WindowAggState: + * - navMaxOffset: max backward (PREV/LAST) reach; set to -1 (the retain-all + * sentinel) on int64 overflow so advance_nav_mark() disables trim. + * - hasFirstNav / navFirstOffset: forward (FIRST) reach from match_start; + * PG_INT64_MAX means unbounded forward reach. * - * On backward-reach overflow, sets navMaxOffsetKind to - * RPR_NAV_OFFSET_RETAIN_ALL so that tuplestore trim is disabled for - * backward navigation. + * Called unconditionally for windows; a non-RPR window has an empty DEFINE + * clause and falls through to the defaults. */ static void eval_define_offsets(WindowAggState *winstate, List *defineClause) { EvalDefineOffsetsContext ctx; NavTraversal trav; - bool needsMax = (winstate->navMaxOffsetKind == RPR_NAV_OFFSET_NEEDS_EVAL); - bool needsFirst = (winstate->hasFirstNav && - winstate->navFirstOffsetKind == RPR_NAV_OFFSET_NEEDS_EVAL); - if (!needsMax && !needsFirst) + /* Defaults: no backward, no FIRST navigation */ + winstate->navMaxOffset = 0; + winstate->hasFirstNav = false; + winstate->navFirstOffset = 0; + + if (defineClause == NIL) return; ctx.winstate = winstate; ctx.maxOffset = 0; ctx.maxOverflow = false; ctx.minFirstOffset = PG_INT64_MAX; + ctx.hasFirst = false; trav.visit = visit_nav_exec; trav.data = &ctx; @@ -4186,23 +4180,21 @@ eval_define_offsets(WindowAggState *winstate, List *defineClause) nav_traversal_walker((Node *) te->expr, &trav); } - if (needsMax) - { - if (ctx.maxOverflow) - { - winstate->navMaxOffsetKind = RPR_NAV_OFFSET_RETAIN_ALL; - winstate->navMaxOffset = 0; - } - else - { - winstate->navMaxOffsetKind = RPR_NAV_OFFSET_FIXED; - winstate->navMaxOffset = ctx.maxOffset; - } - } + /* + * Backward (PREV/LAST) reach. On int64 overflow the lookback cannot be + * bounded, so store the retain-all sentinel (-1); advance_nav_mark() + * reads it to disable tuplestore trim. + */ + if (ctx.maxOverflow) + winstate->navMaxOffset = -1; + else + winstate->navMaxOffset = ctx.maxOffset; - if (needsFirst) + /* Forward (FIRST) reach; never needs a retain-all sentinel */ + winstate->hasFirstNav = ctx.hasFirst; + + if (ctx.hasFirst) { - winstate->navFirstOffsetKind = RPR_NAV_OFFSET_FIXED; if (ctx.minFirstOffset < PG_INT64_MAX) winstate->navFirstOffset = ctx.minFirstOffset; else @@ -4384,13 +4376,13 @@ advance_nav_mark(WindowAggState *winstate, int64 currentPos) if (winstate->nav_winobj == NULL) return; - /* RETAIN_ALL disables trim for the backward (PREV/LAST) dimension */ - if (winstate->navMaxOffsetKind == RPR_NAV_OFFSET_RETAIN_ALL) + /* + * navMaxOffset < 0 is the retain-all sentinel (infinite offset): trim is + * disabled + */ + if (winstate->navMaxOffset < 0) return; - /* navMax is FIXED here: NEEDS_EVAL resolved, RETAIN_ALL returned */ - Assert(winstate->navMaxOffsetKind == RPR_NAV_OFFSET_FIXED); - if (currentPos > winstate->navMaxOffset) navmarkpos = currentPos - winstate->navMaxOffset; else @@ -4400,9 +4392,6 @@ advance_nav_mark(WindowAggState *winstate, int64 currentPos) { int64 firstreach; - /* navFirst is always FIXED; it never takes RETAIN_ALL */ - Assert(winstate->navFirstOffsetKind == RPR_NAV_OFFSET_FIXED); - /* * Head context has the smallest matchStartRow (contexts appended in * nondecreasing order), so bounding by it covers every FIRST reach. diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d860fb9711..94d9a45bdc 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -293,9 +293,6 @@ static WindowAgg *make_windowagg(List *tlist, WindowClause *wc, List *runCondition, RPRPattern *compiledPattern, Bitmapset *defineMatchStartDependent, - RPRNavOffsetKind navMaxOffsetKind, int64 navMaxOffset, - bool hasFirstNav, - RPRNavOffsetKind navFirstOffsetKind, int64 navFirstOffset, List *qual, bool topWindow, Plan *lefttree); static Group *make_group(List *tlist, List *qual, int numGroupCols, @@ -2478,78 +2475,28 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path) * * The driver sets curVarIdx to the index of the variable being walked * before each invocation; the walker uses it to populate matchStartDependent. + * + * XXX: TODO, the above comments need change. */ typedef struct DefineMetadataContext { - int64 maxOffset; /* max PREV/LAST backward offset (>= 0) */ - bool maxNeedsEval; /* non-constant PREV/LAST offset found */ - bool maxOverflow; /* constant offset overflow detected */ - int64 firstOffset; /* min FIRST offset (may be negative for - * PREV_FIRST) */ - bool hasFirst; /* any FIRST node found */ - bool firstNeedsEval; /* non-constant FIRST offset found */ int curVarIdx; /* DEFINE variable currently being walked */ Bitmapset *matchStartDependent; /* variables that depend on * match_start */ } DefineMetadataContext; -/* - * Helper: extract constant offset from an expression, handling NULL/negative. - * If expr is NULL, returns defaultOffset. - * Returns true if constant, false if non-constant (Param, cast, etc.). - */ -static bool -extract_const_offset(Expr *expr, int64 defaultOffset, int64 *result) -{ - if (expr == NULL) - { - *result = defaultOffset; - return true; - } - - if (IsA(expr, Const)) - { - Const *c = (Const *) expr; - - if (c->constisnull) - *result = 0; /* runtime error; safe placeholder */ - else - { - *result = DatumGetInt64(c->constvalue); - if (*result < 0) - *result = 0; /* runtime error; safe placeholder */ - } - return true; - } - - return false; /* non-constant */ -} - /* * visit_nav_plan * nav_traversal_walker callback (NavVisitFn) for the planner side. * At each RPRNavExpr in a DEFINE expression, computes: * - * 1. backward reach (maxOffset) for tuplestore trim: - * - PREV(v, N), LAST(v, N) -> N (default 1) - * - compound PREV_LAST(v, N, M) -> N + M (overflow -> maxOverflow) - * - compound NEXT_LAST(v, N, M) -> max(N - M, 0) - * - * 2. forward reach (firstOffset) for tuplestore trim: - * - FIRST(v, N) -> N (default 0) - * - compound PREV_FIRST(v, N, M) -> N - M (may be negative) - * - compound NEXT_FIRST(v, N, M) -> N + M + * per-variable match_start dependency for absorption suppression: outer nav + * kinds that reach match_start (FIRST, LAST-with-offset, PREV_FIRST, + * NEXT_FIRST, PREV_LAST/NEXT_LAST-with-offset) add curVarIdx to + * matchStartDependent. * - * 3. per-variable match_start dependency for absorption suppression: - * outer nav kinds that reach match_start (FIRST, LAST-with-offset, - * PREV_FIRST, NEXT_FIRST, PREV_LAST/NEXT_LAST-with-offset) add - * curVarIdx to matchStartDependent. - * - * Constant offsets are extracted via extract_const_offset; non-constant - * offsets set maxNeedsEval / firstNeedsEval so the executor can resolve - * them at init time (see visit_nav_exec). Classification uses only the - * outer nav kind: parser nesting restrictions prevent FIRST/LAST inside - * a PREV/NEXT value subexpression. + * Classification uses only the outer nav kind: parser nesting restrictions + * prevent FIRST/LAST inside a PREV/NEXT value subexpression. */ static void visit_nav_plan(NavTraversal *t, RPRNavExpr *nav) @@ -2568,130 +2515,6 @@ visit_nav_plan(NavTraversal *t, RPRNavExpr *nav) Assert(nav->compound_offset_arg == NULL || !IsA(nav->compound_offset_arg, RPRNavExpr)); - /* - * Simple PREV(v, N) and LAST(v, N): backward reach from currentpos. LAST - * without offset = currentpos, no backward reach. NEXT: forward only, - * irrelevant for trim. - */ - if (nav->kind == RPR_NAV_PREV || - (nav->kind == RPR_NAV_LAST && nav->offset_arg != NULL)) - { - if (!context->maxNeedsEval) - { - int64 offset; - - /* - * default 1 is for PREV; the guarded LAST sub-case never uses it. - */ - if (extract_const_offset(nav->offset_arg, 1, &offset)) - context->maxOffset = Max(context->maxOffset, offset); - else - context->maxNeedsEval = true; - } - } - - /* - * Simple FIRST(v, N): forward reach from match_start. Smaller N means - * older rows needed. - */ - if (nav->kind == RPR_NAV_FIRST) - { - context->hasFirst = true; - - if (!context->firstNeedsEval) - { - int64 offset; - - if (extract_const_offset(nav->offset_arg, 0, &offset)) - context->firstOffset = Min(context->firstOffset, offset); - else - context->firstNeedsEval = true; - } - } - - /* - * Compound PREV_LAST / NEXT_LAST: base = currentpos. PREV_LAST(v, N, M): - * target = currentpos - N - M -> lookback = N + M NEXT_LAST(v, N, M): - * target = currentpos - N + M -> lookback = max(N - M, 0) - */ - if (nav->kind == RPR_NAV_PREV_LAST || - nav->kind == RPR_NAV_NEXT_LAST) - { - if (!context->maxNeedsEval) - { - int64 inner; - int64 outer; - int64 reach; - - if (extract_const_offset(nav->offset_arg, 0, &inner) && - extract_const_offset(nav->compound_offset_arg, 1, &outer)) - { - if (nav->kind == RPR_NAV_PREV_LAST) - { - if (pg_add_s64_overflow(inner, outer, &reach)) - context->maxOverflow = true; - else - context->maxOffset = Max(context->maxOffset, reach); - } - else - { - reach = Max(inner - outer, 0); - context->maxOffset = Max(context->maxOffset, reach); - } - } - else - context->maxNeedsEval = true; - } - } - - /* - * Compound PREV_FIRST / NEXT_FIRST: base = match_start. PREV_FIRST(v, N, - * M): target = match_start + N - M NEXT_FIRST(v, N, M): target = - * match_start + N + M The combined offset (N+/-M) from match_start can be - * negative, meaning rows before match_start are needed. - */ - if (nav->kind == RPR_NAV_PREV_FIRST || - nav->kind == RPR_NAV_NEXT_FIRST) - { - context->hasFirst = true; - - if (!context->firstNeedsEval) - { - int64 inner; - int64 outer; - int64 reach; - - if (extract_const_offset(nav->offset_arg, 0, &inner) && - extract_const_offset(nav->compound_offset_arg, 1, &outer)) - { - if (nav->kind == RPR_NAV_PREV_FIRST) - { - /* - * reach = inner - outer. Both are non-negative, so the - * result >= -PG_INT64_MAX, which cannot underflow int64. - * No overflow check needed. - */ - reach = inner - outer; - } - else - { - /* - * NEXT_FIRST: reach = inner + outer. This can overflow, - * but the result is always >= 0, so it never updates - * firstOffset (which tracks the minimum). Clamp to - * PG_INT64_MAX on overflow. - */ - if (pg_add_s64_overflow(inner, outer, &reach)) - reach = PG_INT64_MAX; - } - - context->firstOffset = Min(context->firstOffset, reach); - } - else - context->firstNeedsEval = true; - } - } - /* * Match-start dependency: classify the outer nav kind. A constant * LAST(x, 0) is conservatively included (offset_arg is a non-NULL Const), @@ -2712,36 +2535,24 @@ visit_nav_plan(NavTraversal *t, RPRNavExpr *nav) /* * compute_define_metadata - * Compute navigation offsets and match_start dependency for the - * DEFINE clause in a single pass per variable. + * Classify which DEFINE variables depend on the match start. + * + * Walks each DEFINE variable expression once and returns the set of variable + * indices whose navigation reaches match_start (FIRST, LAST-with-offset, or + * compound PREV_FIRST/NEXT_FIRST/PREV_LAST/NEXT_LAST-with-offset). Such + * variables require per-context re-evaluation during NFA processing, and + * their presence disqualifies the pattern from context absorption. * - * Walks each DEFINE variable expression once, computing: - * - maxOffset: max backward reach from PREV, LAST-with-offset, - * compound PREV_LAST/NEXT_LAST - * - hasFirst/firstOffset: min forward-from-match-start reach from - * FIRST, compound PREV_FIRST/NEXT_FIRST - * - matchStartDependent: bitmapset of variable indices whose - * expressions contain navigation that depends on match_start - * (FIRST, LAST-with-offset, or compound PREV_FIRST/NEXT_FIRST/ - * PREV_LAST/NEXT_LAST-with-offset). Such variables require - * per-context re-evaluation during NFA processing. + * Navigation offsets for tuplestore trim are not computed here; they are + * resolved at executor init (eval_define_offsets), which can evaluate + * non-constant offsets that the planner cannot fold. */ static void -compute_define_metadata(List *defineClause, - RPRNavOffsetKind *maxKind, int64 *maxResult, - bool *hasFirst, - RPRNavOffsetKind *firstKind, int64 *firstResult, - Bitmapset **matchStartDependent) +compute_define_metadata(List *defineClause, Bitmapset **matchStartDependent) { DefineMetadataContext ctx; NavTraversal trav; - ctx.maxOffset = 0; - ctx.maxNeedsEval = false; - ctx.maxOverflow = false; - ctx.firstOffset = PG_INT64_MAX; /* sentinel: no FIRST found yet */ - ctx.hasFirst = false; - ctx.firstNeedsEval = false; ctx.curVarIdx = 0; ctx.matchStartDependent = NULL; @@ -2755,45 +2566,6 @@ compute_define_metadata(List *defineClause, } *matchStartDependent = ctx.matchStartDependent; - - /* Max backward offset */ - if (ctx.maxOverflow) - { - *maxKind = RPR_NAV_OFFSET_RETAIN_ALL; - *maxResult = 0; - } - else if (ctx.maxNeedsEval) - { - *maxKind = RPR_NAV_OFFSET_NEEDS_EVAL; - *maxResult = 0; - } - else - { - *maxKind = RPR_NAV_OFFSET_FIXED; - *maxResult = ctx.maxOffset; - } - - /* First offset (can be negative for compound PREV_FIRST) */ - *hasFirst = ctx.hasFirst; - if (ctx.hasFirst) - { - if (ctx.firstNeedsEval) - { - *firstKind = RPR_NAV_OFFSET_NEEDS_EVAL; - *firstResult = 0; - } - else - { - *firstKind = RPR_NAV_OFFSET_FIXED; - *firstResult = ctx.firstOffset; /* may be negative; PG_INT64_MAX - * if overflowed */ - } - } - else - { - *firstKind = RPR_NAV_OFFSET_FIXED; - *firstResult = 0; - } } /* @@ -2822,12 +2594,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) ListCell *lc; RPRPattern *compiledPattern = NULL; Bitmapset *matchStartDependent = NULL; - RPRNavOffsetKind navMaxOffsetKind = RPR_NAV_OFFSET_FIXED; - int64 navMaxOffset = 0; - bool hasFirstNav = false; - RPRNavOffsetKind navFirstOffsetKind = RPR_NAV_OFFSET_FIXED; - int64 navFirstOffset = 0; - /* * Choice of tlist here is motivated by the fact that WindowAgg will be @@ -2882,15 +2648,11 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) if (wc->rpPattern) { /* - * Walk DEFINE once: collect nav offsets (for tuplestore trim) and the - * bitmapset of match_start-dependent variables (for absorption - * suppression in buildRPRPattern). + * Classify which DEFINE variables depend on match_start (for + * absorption suppression in buildRPRPattern). Nav offsets for + * tuplestore trim are resolved later, at executor init. */ - compute_define_metadata(wc->defineClause, - &navMaxOffsetKind, &navMaxOffset, - &hasFirstNav, - &navFirstOffsetKind, &navFirstOffset, - &matchStartDependent); + compute_define_metadata(wc->defineClause, &matchStartDependent); /* Compile and optimize RPR patterns */ compiledPattern = buildRPRPattern(wc, !bms_is_empty(matchStartDependent)); @@ -2910,11 +2672,6 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path) best_path->runCondition, compiledPattern, matchStartDependent, - navMaxOffsetKind, - navMaxOffset, - hasFirstNav, - navFirstOffsetKind, - navFirstOffset, best_path->qual, best_path->topwindow, subplan); @@ -6988,9 +6745,6 @@ make_windowagg(List *tlist, WindowClause *wc, List *runCondition, RPRPattern *compiledPattern, Bitmapset *defineMatchStartDependent, - RPRNavOffsetKind navMaxOffsetKind, int64 navMaxOffset, - bool hasFirstNav, - RPRNavOffsetKind navFirstOffsetKind, int64 navFirstOffset, List *qual, bool topWindow, Plan *lefttree) { WindowAgg *node = makeNode(WindowAgg); @@ -7028,13 +6782,6 @@ make_windowagg(List *tlist, WindowClause *wc, /* Store pre-computed match_start dependency bitmapset */ node->defineMatchStartDependent = defineMatchStartDependent; - /* Store pre-computed nav offsets for tuplestore trim optimization */ - node->navMaxOffsetKind = navMaxOffsetKind; - node->navMaxOffset = navMaxOffset; - node->hasFirstNav = hasFirstNav; - node->navFirstOffsetKind = navFirstOffsetKind; - node->navFirstOffset = navFirstOffset; - plan->targetlist = tlist; plan->lefttree = lefttree; plan->righttree = NULL; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index db66ebe313..0561fd8f40 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -703,9 +703,9 @@ typedef struct ExprEvalStep struct { WindowAggState *winstate; - RPRNavKind kind; /* navigation kind (simple or compound) */ - Datum *offset_value; /* offset value(s), or NULL */ - bool *offset_isnull; /* offset null flag(s) */ + RPRNavKind kind; + int64 offset; + int64 compound_offset; /* For compound nav: offset_value[0] = inner, [1] = outer */ int16 resulttyplen; /* RESTORE: result type length */ bool resulttypbyval; /* RESTORE: result pass-by-value? */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index d6ac6b229c..ad1371862a 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2726,14 +2726,16 @@ typedef struct WindowAggState TupleTableSlot *temp_slot_2; /* RPR navigation */ - RPRNavOffsetKind navMaxOffsetKind; /* status of navMaxOffset */ - int64 navMaxOffset; /* max backward nav offset (when FIXED) */ + int64 navMaxOffset; /* max backward nav offset, -1 means infinite + * offset, retain all */ bool hasFirstNav; /* FIRST() present in DEFINE */ - RPRNavOffsetKind navFirstOffsetKind; /* status of navFirstOffset */ int64 navFirstOffset; /* min FIRST() offset (when FIXED) */ struct WindowObjectData *nav_winobj; /* winobj for RPR nav fetch */ int64 nav_slot_pos; /* position cached in nav_slot, or -1 */ - TupleTableSlot *nav_slot; /* slot for PREV/NEXT/FIRST/LAST target row */ + TupleTableSlot *nav_slot; /* slot holding the resolved navigation target + * row (simple or compound + * PREV/NEXT/FIRST/LAST) */ + TupleTableSlot *nav_saved_outertuple; /* saved slot during nav swap */ TupleTableSlot *nav_null_slot; /* all NULL slot */ int64 nav_match_start; /* match_start for FIRST/LAST nav */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 131c8d4ce8..ab75964755 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -594,21 +594,6 @@ typedef enum RPSkipTo ST_PAST_LAST_ROW, /* SKIP TO PAST LAST ROW */ } RPSkipTo; -/* - * RPRNavOffsetKind - status of navigation offset for tuplestore trim. - * - * The planner computes navMaxOffset/navFirstOffset for tuplestore mark - * optimization. This enum tracks whether the value is a resolved constant, - * needs runtime evaluation, or cannot be determined (retain all rows). - */ -typedef enum RPRNavOffsetKind -{ - RPR_NAV_OFFSET_FIXED, /* resolved constant; use the offset value */ - RPR_NAV_OFFSET_NEEDS_EVAL, /* non-constant offset; evaluate at executor - * init */ - RPR_NAV_OFFSET_RETAIN_ALL, /* cannot determine; retain all rows (no trim) */ -} RPRNavOffsetKind; - /* * RPRPatternNodeType - Row Pattern Recognition pattern node types */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 2b37c8f226..b4a196e1f6 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1410,27 +1410,6 @@ typedef struct WindowAgg */ Bitmapset *defineMatchStartDependent; - /* - * Navigation offset status and values for tuplestore mark optimization. - * See RPRNavOffsetKind in nodes/parsenodes.h. - * - * navMaxOffset: maximum backward reach from currentpos (contributed by - * PREV, LAST-with-offset, compound PREV_LAST/NEXT_LAST). Only valid when - * navMaxOffsetKind == RPR_NAV_OFFSET_FIXED. - * - * navFirstOffset: minimum forward offset from match_start (contributed by - * FIRST, compound PREV_FIRST/NEXT_FIRST). Can be negative for compound - * PREV_FIRST. Only valid when navFirstOffsetKind == RPR_NAV_OFFSET_FIXED - * and hasFirstNav == true. - */ - RPRNavOffsetKind navMaxOffsetKind; - int64 navMaxOffset; - - /* true if FIRST-based navigation (FIRST, PREV_FIRST, NEXT_FIRST) is used */ - bool hasFirstNav; - RPRNavOffsetKind navFirstOffsetKind; - int64 navFirstOffset; - /* * false for all apart from the WindowAgg that's closest to the root of * the plan diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 8576a3c9c5..902521ff83 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -668,25 +668,40 @@ typedef struct WindowFuncRunCondition */ typedef enum RPRNavKind { - RPR_NAV_PREV, - RPR_NAV_NEXT, - RPR_NAV_FIRST, - RPR_NAV_LAST, + RPR_NAV_PREV, /* offset default: 1 */ + RPR_NAV_NEXT, /* offset default: 1 */ + RPR_NAV_FIRST, /* offset default: 0 */ + RPR_NAV_LAST, /* offset default: 0 */ /* compound: outer(inner(arg)) */ - RPR_NAV_PREV_FIRST, - RPR_NAV_PREV_LAST, - RPR_NAV_NEXT_FIRST, - RPR_NAV_NEXT_LAST, + RPR_NAV_PREV_FIRST, /* (offset, compound_offset) default: (0, 1) */ + RPR_NAV_PREV_LAST, /* (offset, compound_offset) default: (0, 1) */ + RPR_NAV_NEXT_FIRST, /* (offset, compound_offset) default: (0, 1) */ + RPR_NAV_NEXT_LAST, /* (offset, compound_offset) default: (0, 1) */ } RPRNavKind; typedef struct RPRNavExpr { Expr xpr; - RPRNavKind kind; /* navigation kind */ - Expr *arg; /* argument expression */ - Expr *offset_arg; /* offset expression, or NULL for default */ - Expr *compound_offset_arg; /* outer offset for compound nav, or - * NULL if simple */ + RPRNavKind kind; + /* argument expression */ + Expr *arg; + /* offset expression */ + Expr *offset_arg; + /* outer offset for compound navigation */ + Expr *compound_offset_arg; + + /* + * The computed offset value, for default value see RPRNavKind comments + * above. The value will be set in visit_nav_exec. + */ + int64 offset; + + /* + * The computeed compound_offset offset value, for default value see + * RPRNavKind comments above. The value will be set in visit_nav_exec. + */ + int64 compound_offset; + /* result type (same as arg's type) */ Oid resulttype pg_node_attr(query_jumble_ignore); /* OID of collation of result */ diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out index d8343c9acc..77afb5e1ed 100644 --- a/src/test/regress/expected/rpr_explain.out +++ b/src/test/regress/expected/rpr_explain.out @@ -5775,7 +5775,7 @@ EXPLAIN (COSTS OFF) EXECUTE rpr_nav_offset_prep(2); WindowAgg Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) Pattern: a+" - Nav Mark Lookback: runtime + Nav Mark Lookback: 2 -> Function Scan on generate_series s (5 rows) @@ -5974,7 +5974,7 @@ EXPLAIN (COSTS OFF) EXECUTE test_overflow_lookback(4611686018427387904, 46116860 WindowAgg Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) Pattern: a+ - Nav Mark Lookback: runtime + Nav Mark Lookback: retain all -> Function Scan on generate_series s (5 rows) @@ -6042,7 +6042,7 @@ EXPLAIN (COSTS OFF) EXECUTE p_first_runtime(1, 1); Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) Pattern: a+ Nav Mark Lookback: 0 - Nav Mark Lookahead: runtime + Nav Mark Lookahead: 2 -> Function Scan on generate_series s (6 rows) diff --git a/src/test/regress/expected/rpr_integration.out b/src/test/regress/expected/rpr_integration.out index 541ad35de4..ec335fd733 100644 --- a/src/test/regress/expected/rpr_integration.out +++ b/src/test/regress/expected/rpr_integration.out @@ -1077,7 +1077,7 @@ EXPLAIN (COSTS OFF) EXECUTE rpr_prev(1); WindowAgg Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) Pattern: a b+ - Nav Mark Lookback: runtime + Nav Mark Lookback: 1 -> Sort Sort Key: id -> Seq Scan on rpr_integ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index c7c456fa64..6adee8b018 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2539,7 +2539,6 @@ RPRNFAContext RPRNFAState RPRNavExpr RPRNavKind -RPRNavOffsetKind RPRPattern RPRPatternElement RPRPatternNode -- 2.34.1