From cab28ed773c49a2aac53b0d3e9686b6a60b5cb84 Mon Sep 17 00:00:00 2001 From: jian he Date: Thu, 2 Jul 2026 17:36:49 +0800 Subject: [PATCH v50 1/4] Refactor visit_nav_plan Previously the RPR DEFINE clause was walked twice for navigation offsets: visit_nav_plan() did something with constant offsets, while visit_nav_exec() did something for non-constant offsets at ExecInitWindowAgg. The only thing the planner really need produce is the match_start-dependency classification, because buildRPRPattern() consumes it to decide whether context absorption is safe and bakes the result into the compiled pattern. EXPLAIN without ANALYZE also executes ExecInitWindowAgg, computing both navigation and compound offsets there is sufficient. The plan-time offset folding is not needed. The tuplestore is built at executor init, so the trim offsets only have to be known by then, and visit_nav_exec() already evaluates every offset expression there (constants included). So narrow visit_nav_plan() (and compute_define_metadata()) to that single classification pass, and move all offset computation into eval_define_offsets() at ExecInitWindowAgg. remove 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; remove below struct WindowAgg field { int64 navMaxOffset; /* true if FIRST-based navigation (FIRST, PREV_FIRST, NEXT_FIRST) is used */ bool hasFirstNav; int64 navFirstOffset; } There fields exists in struct WindowAggState also. Based on https://github.com/assam258-5892/postgres/commits/RPR --- src/backend/commands/explain.c | 69 +---- src/backend/executor/nodeWindowAgg.c | 90 +++--- src/backend/optimizer/plan/createplan.c | 292 ++---------------- src/include/nodes/execnodes.h | 5 +- src/include/nodes/parsenodes.h | 15 - src/include/nodes/plannodes.h | 21 -- src/test/regress/expected/rpr_explain.out | 6 +- src/test/regress/expected/rpr_integration.out | 2 +- src/tools/pgindent/typedefs.list | 1 - 9 files changed, 87 insertions(+), 414 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/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index e4e97a6ed9..8995329949 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 */ @@ -4028,6 +4023,7 @@ 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; /* @@ -4115,6 +4111,9 @@ visit_nav_exec(NavTraversal *t, RPRNavExpr *nav) reach = eval_nav_offset_helper(context->winstate, nav->offset_arg, 0); + + context->hasFirst = true; + context->minFirstOffset = Min(context->minFirstOffset, reach); } else if (nav->kind == RPR_NAV_PREV_FIRST || @@ -4144,39 +4143,45 @@ visit_nav_exec(NavTraversal *t, RPRNavExpr *nav) if (pg_add_s64_overflow(inner, outer, &reach)) reach = PG_INT64_MAX; } + context->hasFirst = true; context->minFirstOffset = Min(context->minFirstOffset, reach); } } /* * 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 +4191,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; + + /* Forward (FIRST) reach; never needs a retain-all sentinel */ + winstate->hasFirstNav = ctx.hasFirst; - if (needsFirst) + if (ctx.hasFirst) { - winstate->navFirstOffsetKind = RPR_NAV_OFFSET_FIXED; if (ctx.minFirstOffset < PG_INT64_MAX) winstate->navFirstOffset = ctx.minFirstOffset; else @@ -4384,13 +4387,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 +4403,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..8146ad1070 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,72 +2475,25 @@ 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 - * - * 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. + * 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 @@ -2568,130 +2518,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 +2538,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 +2569,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 +2597,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 +2651,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 +2675,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 +6748,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 +6785,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/nodes/execnodes.h b/src/include/nodes/execnodes.h index d6ac6b229c..1f9508649c 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2726,10 +2726,9 @@ 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 */ 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/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