0005-Disable-context-absorption-for-limited-frame.patch
application/octet-stream
Filename: 0005-Disable-context-absorption-for-limited-frame.patch
Type: application/octet-stream
Part: 4
Message:
Re: Row pattern recognition
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0005
Subject: Row pattern recognition: Disable context absorption for limited frame
| File | + | − |
|---|---|---|
| src/backend/executor/nodeWindowAgg.c | 5 | 3 |
| src/test/regress/expected/rpr.out | 27 | 0 |
| src/test/regress/sql/rpr.sql | 20 | 0 |
From 3cdc30c795f918b5925ffdce9dbc91a08fe92a5e Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Fri, 16 Jan 2026 08:22:08 +0900
Subject: [PATCH 5/5] Row pattern recognition: Disable context absorption for
limited frame
---
src/backend/executor/nodeWindowAgg.c | 8 +++++---
src/test/regress/expected/rpr.out | 27 +++++++++++++++++++++++++++
src/test/regress/sql/rpr.sql | 20 ++++++++++++++++++++
3 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index f02bff4251a..5dea161c928 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -4793,7 +4793,7 @@ update_reduced_frame(WindowObject winobj, int64 pos)
{
nfa_finalize_all_contexts(winstate, currentPos - 1);
/* Absorb completed contexts at partition boundary (SKIP PAST LAST ROW only) */
- if (winstate->rpSkipTo == ST_PAST_LAST_ROW)
+ if (winstate->rpSkipTo == ST_PAST_LAST_ROW && !hasLimitedFrame)
nfa_absorb_contexts(winstate, NULL, currentPos - 1);
break;
}
@@ -4812,12 +4812,14 @@ update_reduced_frame(WindowObject winobj, int64 pos)
nfa_start_context(winstate, currentPos + 1);
/*
- * Absorb redundant contexts (SKIP PAST LAST ROW only).
+ * Absorb redundant contexts (SKIP PAST LAST ROW only, unbounded frame).
* At the same elementIndex, if newer context's count <= older context's count,
* the newer context can be absorbed (for unbounded quantifiers).
+ * With limited frame, different contexts have different frame boundaries,
+ * so absorption is not safe.
* Note: Never absorb targetCtx - it's the context we're trying to complete.
*/
- if (winstate->rpSkipTo == ST_PAST_LAST_ROW)
+ if (winstate->rpSkipTo == ST_PAST_LAST_ROW && !hasLimitedFrame)
nfa_absorb_contexts(winstate, targetCtx, currentPos);
/* Check if target context is now complete */
diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out
index f22bba23bff..1567fec953d 100644
--- a/src/test/regress/expected/rpr.out
+++ b/src/test/regress/expected/rpr.out
@@ -929,6 +929,33 @@ WINDOW w AS (
B | 4 | {4}
(4 rows)
+-- Limited frame with absorption test
+-- Row 0: frame [0,2], can't see B at row 3 -> no match
+-- Row 1: frame [1,3], can see A A B -> should match rows 1-3
+WITH frame_absorb_test AS (
+ SELECT * FROM (VALUES
+ (0, 'A'), (1, 'A'), (2, 'A'), (3, 'B')
+ ) AS t(id, flag)
+)
+SELECT id, flag, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM frame_absorb_test
+WINDOW w AS (
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+ B)
+ DEFINE
+ A AS flag = 'A',
+ B AS flag = 'B'
+);
+ id | flag | match_start | match_end
+----+------+-------------+-----------
+ 0 | A | |
+ 1 | A | 1 | 3
+ 2 | A | |
+ 3 | B | |
+(4 rows)
+
-- ROWS BETWEEN CURRENT ROW AND offset FOLLOWING
SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w,
count(*) OVER w
diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql
index 93bf1698fd9..6b70c82b2c7 100644
--- a/src/test/regress/sql/rpr.sql
+++ b/src/test/regress/sql/rpr.sql
@@ -373,6 +373,26 @@ WINDOW w AS (
DEFINE A AS id < 10
);
+-- Limited frame with absorption test
+-- Row 0: frame [0,2], can't see B at row 3 -> no match
+-- Row 1: frame [1,3], can see A A B -> should match rows 1-3
+WITH frame_absorb_test AS (
+ SELECT * FROM (VALUES
+ (0, 'A'), (1, 'A'), (2, 'A'), (3, 'B')
+ ) AS t(id, flag)
+)
+SELECT id, flag, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM frame_absorb_test
+WINDOW w AS (
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+ B)
+ DEFINE
+ A AS flag = 'A',
+ B AS flag = 'B'
+);
+
-- ROWS BETWEEN CURRENT ROW AND offset FOLLOWING
SELECT company, tdate, price, first_value(tdate) OVER w, last_value(tdate) OVER w,
count(*) OVER w
--
2.50.1 (Apple Git-155)