public inbox for [email protected]  
help / color / mirror / Atom feed
From: Henson Choi <[email protected]>
To: 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]
Subject: Re: Row pattern recognition
Date: Fri, 6 Mar 2026 13:38:30 +0900
Message-ID: <CAAAe_zBH=6nh6Yg9EuohthTgzHgtbhtvLYAnabg6mCHQPLLpqQ@mail.gmail.com> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
	<CAE+cgNiUbKeH1A0PoxV2QjpsoxJLe+pJcGz_gdxwOwu_9zqchw@mail.gmail.com>
	<CAAAe_zBFKp7bn9YUamzNiy7s2LQ3C9VXsFLRTyVTbk+ETLfZUQ@mail.gmail.com>
	<[email protected]>

Hi Tatsuo,

I reviewed the planner's Window clause optimizations and found
additional issues with RPR windows.  Thanks to SungJun's Oracle
cross-validation work, all converted regression tests now pass
on both systems.  With these fixes, I'd like to move on to
non-functional quality testing next.

Here are eight incremental patches on top of v44.


nocfbot-0001: Fix elog message to use lowercase per PostgreSQL convention

  Minor style fix: lowercase the elog error message in
  nodeWindowAgg.c to follow PostgreSQL coding convention.


nocfbot-0002: Fix RPR reluctant quantifier flag lost during VIEW
serialization

  The reluctant flag on quantifiers was lost when a VIEW containing
  RPR was serialized and restored via ruleutils.c.  This patch fixes
  gram.y, rpr.c, ruleutils.c, and parsenodes.h so the flag survives
  the round-trip.


nocfbot-0003: Expand RPR test coverage and improve test comments

  Reorganizes test cases across rpr.sql, rpr_base.sql, rpr_explain.sql,
  and rpr_nfa.sql.  Moves base functionality tests from rpr.sql to
  rpr_base.sql and NFA-specific tests to rpr_nfa.sql.  Adds better
  section comments throughout.  Duplicate tests in rpr.sql were
  removed.  No reduction in test coverage.


nocfbot-0004: Keep RPR test objects for pg_upgrade/pg_dump testing

  Adjusts rpr_base.sql and rpr_explain.sql so that test tables and
  views are not dropped at the end of the test.  This allows
  pg_upgrade and pg_dump regression testing to cover RPR objects.


nocfbot-0005: Disable run condition pushdown for RPR windows

  Revised version of your run condition fix [1].  Existing test
  expected output updated accordingly.


nocfbot-0006: Disable frame optimization for RPR windows

  Prevents the planner from optimizing the window frame for RPR
  windows.  The frame clause in RPR has different semantics (it
  bounds the pattern search space), so standard frame optimizations
  must be disabled.  Adds EXPLAIN tests to verify.  Please review
  this one -- it's a newly discovered issue.


nocfbot-0007: Add stock scenario tests for RPR pattern matching

  Adds stock trading scenario tests using realistic synthetic data
  (stock.data with 1632 rows).  Tests V-shape recovery, W-shape,
  consecutive rises, and other common pattern matching use cases.


nocfbot-0008: Fix zero-min reluctant quantifier to produce zero-length match

  Fixes the bug reported by SungJun [2]: reluctant quantifiers with
  min=0 (A*?, A??) were incorrectly consuming at least one row instead
  of producing a zero-length match.  The NFA can now internally
  distinguish between a zero-length match and no match, so it will
  be ready when additional infrastructure (e.g. MATCH_NUMBER) is
  added.  Now matches Oracle behavior.


[1]
https://www.postgresql.org/message-id/[email protected]
[2]
https://www.postgresql.org/message-id/[email protected]...


Best regards,
Henson

From f4abb464c187ef6d1334d856db881d40069531da Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 15:49:54 +0900
Subject: [PATCH 1/8] Fix elog message to use lowercase per PostgreSQL
 convention


diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 701e83b519b..4441b4d9c51 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -4710,7 +4710,7 @@ row_is_in_reduced_frame(WindowObject winobj, int64 pos)
 			break;
 
 		default:
-			elog(ERROR, "Unrecognized state: %d at: " INT64_FORMAT,
+			elog(ERROR, "unrecognized state: %d at: " INT64_FORMAT,
 				 state, pos);
 			break;
 	}
-- 
2.50.1 (Apple Git-155)


From 2bc7f29dcfceefc83fd787eb138c96247f565360 Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 15:50:48 +0900
Subject: [PATCH 2/8] Fix RPR reluctant quantifier flag lost during VIEW
 serialization


diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index 8792d8174fa..009c0f5019d 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -198,7 +198,7 @@ flattenSeqChildren(List *children)
 
 		/* GROUP{1,1} should have been unwrapped by optimizeGroupPattern */
 		Assert(!(opt->nodeType == RPR_PATTERN_GROUP &&
-				 opt->min == 1 && opt->max == 1 && opt->reluctant < 0));
+				 opt->min == 1 && opt->max == 1 && opt->reluctant == false));
 
 		if (opt->nodeType == RPR_PATTERN_SEQ)
 		{
@@ -234,7 +234,7 @@ mergeConsecutiveVars(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_VAR && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_VAR && child->reluctant == false)
 		{
 			/* ----------------------
 			 * Can merge consecutive VAR nodes if:
@@ -253,7 +253,7 @@ mergeConsecutiveVars(List *children)
 				 * Merge: accumulate min/max into prev. prev is guaranteed to
 				 * be a non-reluctant VAR by the outer condition.
 				 */
-				Assert(prev->nodeType == RPR_PATTERN_VAR && prev->reluctant < 0);
+				Assert(prev->nodeType == RPR_PATTERN_VAR && prev->reluctant == false);
 
 				prev->min += child->min;
 
@@ -308,7 +308,7 @@ mergeConsecutiveGroups(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant == false)
 		{
 			/* ----------------------
 			 * Can merge consecutive GROUP nodes if:
@@ -327,7 +327,7 @@ mergeConsecutiveGroups(List *children)
 				 * Merge: accumulate min/max into prev. prev is guaranteed to
 				 * be a non-reluctant GROUP by the outer condition.
 				 */
-				Assert(prev->nodeType == RPR_PATTERN_GROUP && prev->reluctant < 0);
+				Assert(prev->nodeType == RPR_PATTERN_GROUP && prev->reluctant == false);
 
 				prev->min += child->min;
 
@@ -385,7 +385,7 @@ mergeConsecutiveAlts(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_ALT && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_ALT && child->reluctant == false)
 		{
 			if (prev != NULL &&
 				rprPatternChildrenEqual(prev->children, child->children))
@@ -406,7 +406,8 @@ mergeConsecutiveAlts(List *children)
 						group->nodeType = RPR_PATTERN_GROUP;
 						group->min = count;
 						group->max = count;
-						group->reluctant = -1;
+						group->reluctant = false;
+						group->reluctant_location = -1;
 						group->location = -1;
 						group->children = list_make1(prev);
 						mergedChildren = lappend(mergedChildren, group);
@@ -430,7 +431,8 @@ mergeConsecutiveAlts(List *children)
 					group->nodeType = RPR_PATTERN_GROUP;
 					group->min = count;
 					group->max = count;
-					group->reluctant = -1;
+					group->reluctant = false;
+					group->reluctant_location = -1;
 					group->location = -1;
 					group->children = list_make1(prev);
 					mergedChildren = lappend(mergedChildren, group);
@@ -454,7 +456,8 @@ mergeConsecutiveAlts(List *children)
 			group->nodeType = RPR_PATTERN_GROUP;
 			group->min = count;
 			group->max = count;
-			group->reluctant = -1;
+			group->reluctant = false;
+			group->reluctant_location = -1;
 			group->location = -1;
 			group->children = list_make1(prev);
 			mergedChildren = lappend(mergedChildren, group);
@@ -498,7 +501,7 @@ mergeGroupPrefixSuffix(List *children)
 		 * children. GROUP's content may be wrapped in a SEQ - unwrap for
 		 * comparison.
 		 */
-		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant == false)
 		{
 			List	   *groupContent = child->children;
 			int			groupChildCount;
@@ -773,14 +776,14 @@ tryMultiplyQuantifiers(RPRPatternNode *pattern)
 	/* Parser always creates GROUP with exactly one child */
 	Assert(list_length(pattern->children) == 1);
 
-	if (pattern->reluctant >= 0)
+	if (pattern->reluctant)
 		return pattern;
 
 	child = (RPRPatternNode *) linitial(pattern->children);
 
 	if ((child->nodeType != RPR_PATTERN_VAR &&
 		 child->nodeType != RPR_PATTERN_GROUP) ||
-		child->reluctant >= 0)
+		child->reluctant)
 		return pattern;
 
 	/* Case 1: Both unbounded - (A*)* -> A*, (A+)+ -> A+ */
@@ -862,11 +865,12 @@ tryUnwrapGroup(RPRPatternNode *pattern)
 	 * the child and unwrap.  E.g., (A)?? -> A??, (A)+? -> A+?
 	 */
 	if (child->nodeType == RPR_PATTERN_VAR &&
-		child->min == 1 && child->max == 1 && child->reluctant < 0)
+		child->min == 1 && child->max == 1 && child->reluctant == false)
 	{
 		child->min = pattern->min;
 		child->max = pattern->max;
 		child->reluctant = pattern->reluctant;
+		child->reluctant_location = pattern->reluctant_location;
 		return child;
 	}
 
@@ -1150,7 +1154,7 @@ fillRPRPatternVar(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
 	elem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 	elem->next = RPR_ELEMIDX_INVALID;
 	elem->jump = RPR_ELEMIDX_INVALID;
-	if (node->reluctant >= 0)
+	if (node->reluctant)
 		elem->flags |= RPR_ELEM_RELUCTANT;
 	(*idx)++;
 
@@ -1189,7 +1193,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		elem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 		elem->next = RPR_ELEMIDX_INVALID;	/* set by finalize */
 		elem->jump = RPR_ELEMIDX_INVALID;	/* set after END */
-		if (node->reluctant >= 0)
+		if (node->reluctant)
 			elem->flags |= RPR_ELEM_RELUCTANT;
 		(*idx)++;
 		groupStartIdx = *idx;	/* children start after BEGIN */
@@ -1214,7 +1218,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		endElem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 		endElem->next = RPR_ELEMIDX_INVALID;
 		endElem->jump = groupStartIdx;	/* loop to first child */
-		if (node->reluctant >= 0)
+		if (node->reluctant)
 			endElem->flags |= RPR_ELEM_RELUCTANT;
 
 		/*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b55a11cc837..f1a71cd036b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -16967,7 +16967,8 @@ row_pattern_alt:
 						n->children = list_make2($1, $3);
 						n->min = 1;
 						n->max = 1;
-						n->reluctant = -1;
+						n->reluctant = false;
+						n->reluctant_location = -1;
 						n->location = @1;
 						$$ = (Node *) n;
 					}
@@ -16994,7 +16995,8 @@ row_pattern_seq:
 						n->children = list_make2($1, $2);
 						n->min = 1;
 						n->max = 1;
-						n->reluctant = -1;
+						n->reluctant = false;
+						n->reluctant_location = -1;
 						n->location = @1;
 						$$ = (Node *) n;
 					}
@@ -17010,6 +17012,7 @@ row_pattern_term:
 					n->min = q->min;
 					n->max = q->max;
 					n->reluctant = q->reluctant;
+					n->reluctant_location = q->reluctant_location;
 					$$ = (Node *) n;
 				}
 		;
@@ -17022,7 +17025,8 @@ row_pattern_primary:
 					n->varName = $1;
 					n->min = 1;
 					n->max = 1;
-					n->reluctant = -1;
+					n->reluctant = false;
+					n->reluctant_location = -1;
 					n->children = NIL;
 					n->location = @1;
 					$$ = (Node *) n;
@@ -17035,7 +17039,8 @@ row_pattern_primary:
 					n->children = list_make1(inner);
 					n->min = 1;
 					n->max = 1;
-					n->reluctant = -1;
+					n->reluctant = false;
+					n->reluctant_location = -1;
 					n->location = @1;
 					$$ = (Node *) n;
 				}
@@ -20400,14 +20405,15 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
  *		Create an RPRPatternNode with specified quantifier bounds.
  */
 static RPRPatternNode *
-makeRPRQuantifier(int min, int max, ParseLoc reluctant, int location,
+makeRPRQuantifier(int min, int max, ParseLoc reluctant_location, int location,
 				  core_yyscan_t yyscanner)
 {
 	RPRPatternNode *n = makeNode(RPRPatternNode);
 
 	n->min = min;
 	n->max = max;
-	n->reluctant = reluctant;
+	n->reluctant = (reluctant_location >= 0);
+	n->reluctant_location = reluctant_location;
 	n->location = location;
 	return n;
 }
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f5bb81e8c05..928bed2e9fb 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6778,7 +6778,7 @@ append_pattern_quantifier(StringInfo buf, RPRPatternNode *node)
 	else
 		appendStringInfo(buf, "{%d,%d}", node->min, node->max);
 
-	if (node->reluctant >= 0)
+	if (node->reluctant)
 	{
 		if (!has_quantifier)
 			appendStringInfo(buf, "{1}");	/* make reluctant ? unambiguous */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8af527b57d3..22e856c671d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -608,8 +608,8 @@ typedef struct RPRPatternNode
 	RPRPatternNodeType nodeType;	/* VAR, SEQ, ALT, GROUP */
 	int			min;			/* minimum repetitions (0 for *, ?) */
 	int			max;			/* maximum repetitions (INT_MAX for *, +) */
-	ParseLoc	reluctant;		/* location of '?' for reluctant, -1 for
-								 * greedy */
+	bool		reluctant;		/* true for reluctant (non-greedy) */
+	ParseLoc	reluctant_location; /* location of '?' token, or -1 */
 	ParseLoc	location;		/* token location, or -1 */
 	char	   *varName;		/* VAR: variable name */
 	List	   *children;		/* SEQ, ALT, GROUP: child nodes */
-- 
2.50.1 (Apple Git-155)


From 350f3234f318059826a2b2bd96e91e4d7fa22253 Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 17:06:59 +0900
Subject: [PATCH 3/8] Expand RPR test coverage and improve test comments


diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out
index 8af44392700..35b90a04492 100644
--- a/src/test/regress/expected/rpr.out
+++ b/src/test/regress/expected/rpr.out
@@ -1,5 +1,10 @@
 --
--- Test for row pattern definition clause
+-- Test for row pattern recognition: WINDOW clause integration and
+-- real-world scenario tests using stock price data.
+--
+-- Parser/planner tests: rpr_base.sql
+-- NFA engine tests: rpr_nfa.sql
+-- EXPLAIN statistics tests: rpr_explain.sql
 --
 CREATE TEMP TABLE stock (
        company TEXT,
@@ -51,6 +56,9 @@ SELECT * FROM stock;
  company2 | 07-10-2023 |  1300
 (20 rows)
 
+--
+-- Basic pattern matching with PREV/NEXT
+--
 -- basic test using PREV
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w,
  nth_value(tdate, 2) OVER w AS nth_second
@@ -426,7 +434,7 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
  company2 | 07-10-2023 |  1300 |             |           
 (20 rows)
 
--- basic test with none-greedy pattern
+-- basic test with fixed-length pattern (A A A = exactly 3)
 SELECT company, tdate, price, count(*) OVER w
  FROM stock
  WINDOW w AS (
@@ -752,7 +760,9 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
  company2 | 07-10-2023 |  1300 |             |           
 (20 rows)
 
--- using AFTER MATCH SKIP TO NEXT ROW
+-- using AFTER MATCH SKIP TO NEXT ROW (same pattern as above;
+-- match length is always 2, so result is identical to SKIP PAST LAST ROW.
+-- SKIP TO NEXT ROW's distinct effect is tested in backtracking section.)
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
  FROM stock
  WINDOW w AS (
@@ -789,6 +799,179 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
  company2 | 07-10-2023 |  1300 |             |           
 (20 rows)
 
+-- PREV returns NULL at partition's first row (null_slot path)
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ PATTERN (BOUNDARY REST+)
+ DEFINE
+  BOUNDARY AS PREV(price) IS NULL,
+  REST AS PREV(price) IS NOT NULL
+);
+ company  |   tdate    | price | count 
+----------+------------+-------+-------
+ company1 | 07-01-2023 |   100 |    10
+ company1 | 07-02-2023 |   200 |     0
+ company1 | 07-03-2023 |   150 |     0
+ company1 | 07-04-2023 |   140 |     0
+ company1 | 07-05-2023 |   150 |     0
+ company1 | 07-06-2023 |    90 |     0
+ company1 | 07-07-2023 |   110 |     0
+ company1 | 07-08-2023 |   130 |     0
+ company1 | 07-09-2023 |   120 |     0
+ company1 | 07-10-2023 |   130 |     0
+ company2 | 07-01-2023 |    50 |    10
+ company2 | 07-02-2023 |  2000 |     0
+ company2 | 07-03-2023 |  1500 |     0
+ company2 | 07-04-2023 |  1400 |     0
+ company2 | 07-05-2023 |  1500 |     0
+ company2 | 07-06-2023 |    60 |     0
+ company2 | 07-07-2023 |  1100 |     0
+ company2 | 07-08-2023 |  1300 |     0
+ company2 | 07-09-2023 |  1200 |     0
+ company2 | 07-10-2023 |  1300 |     0
+(20 rows)
+
+-- NEXT returns NULL at partition's last row (null_slot path)
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+ BOUNDARY)
+ DEFINE
+  A AS NEXT(price) IS NOT NULL,
+  BOUNDARY AS NEXT(price) IS NULL
+);
+ company  |   tdate    | price | count 
+----------+------------+-------+-------
+ company1 | 07-01-2023 |   100 |    10
+ company1 | 07-02-2023 |   200 |     0
+ company1 | 07-03-2023 |   150 |     0
+ company1 | 07-04-2023 |   140 |     0
+ company1 | 07-05-2023 |   150 |     0
+ company1 | 07-06-2023 |    90 |     0
+ company1 | 07-07-2023 |   110 |     0
+ company1 | 07-08-2023 |   130 |     0
+ company1 | 07-09-2023 |   120 |     0
+ company1 | 07-10-2023 |   130 |     0
+ company2 | 07-01-2023 |    50 |    10
+ company2 | 07-02-2023 |  2000 |     0
+ company2 | 07-03-2023 |  1500 |     0
+ company2 | 07-04-2023 |  1400 |     0
+ company2 | 07-05-2023 |  1500 |     0
+ company2 | 07-06-2023 |    60 |     0
+ company2 | 07-07-2023 |  1100 |     0
+ company2 | 07-08-2023 |  1300 |     0
+ company2 | 07-09-2023 |  1200 |     0
+ company2 | 07-10-2023 |  1300 |     0
+(20 rows)
+
+-- DESC order: PREV refers to the row with later date
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate DESC
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (START DOWN+ UP+)
+ DEFINE
+  START AS TRUE,
+  DOWN AS price < PREV(price),
+  UP AS price > PREV(price)
+);
+ company  |   tdate    | price | count 
+----------+------------+-------+-------
+ company1 | 07-10-2023 |   130 |     3
+ company1 | 07-09-2023 |   120 |     0
+ company1 | 07-08-2023 |   130 |     0
+ company1 | 07-07-2023 |   110 |     3
+ company1 | 07-06-2023 |    90 |     0
+ company1 | 07-05-2023 |   150 |     0
+ company1 | 07-04-2023 |   140 |     0
+ company1 | 07-03-2023 |   150 |     0
+ company1 | 07-02-2023 |   200 |     0
+ company1 | 07-01-2023 |   100 |     0
+ company2 | 07-10-2023 |  1300 |     3
+ company2 | 07-09-2023 |  1200 |     0
+ company2 | 07-08-2023 |  1300 |     0
+ company2 | 07-07-2023 |  1100 |     3
+ company2 | 07-06-2023 |    60 |     0
+ company2 | 07-05-2023 |  1500 |     0
+ company2 | 07-04-2023 |  1400 |     0
+ company2 | 07-03-2023 |  1500 |     0
+ company2 | 07-02-2023 |  2000 |     0
+ company2 | 07-01-2023 |    50 |     0
+(20 rows)
+
+-- Multiple partitions with unequal sizes
+WITH multi_part AS (
+ SELECT * FROM (VALUES
+  ('a', 1, 10), ('a', 2, 20), ('a', 3, 15),
+  ('b', 1, 5),
+  ('c', 1, 100), ('c', 2, 200), ('c', 3, 150), ('c', 4, 140), ('c', 5, 300)
+ ) AS t(grp, id, val)
+)
+SELECT grp, id, val, count(*) OVER w
+FROM multi_part
+WINDOW w AS (
+ PARTITION BY grp
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+)
+ DEFINE
+  A AS val <= NEXT(val),
+  B AS val > PREV(val) OR val < PREV(val)
+);
+ grp | id | val | count 
+-----+----+-----+-------
+ a   |  1 |  10 |     3
+ a   |  2 |  20 |     0
+ a   |  3 |  15 |     0
+ b   |  1 |   5 |     0
+ c   |  1 | 100 |     5
+ c   |  2 | 200 |     0
+ c   |  3 | 150 |     0
+ c   |  4 | 140 |     0
+ c   |  5 | 300 |     0
+(9 rows)
+
+-- FLOAT/NUMERIC DEFINE conditions
+WITH float_data AS (
+ SELECT * FROM (VALUES
+  (1, 1.0::float8), (2, 1.5), (3, 1.4999), (4, 1.50001), (5, 0.1)
+ ) AS t(id, val)
+)
+SELECT id, val, count(*) OVER w
+FROM float_data
+WINDOW w AS (
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+)
+ DEFINE
+  A AS TRUE,
+  B AS val > PREV(val) * 0.99
+);
+ id |   val   | count 
+----+---------+-------
+  1 |       1 |     4
+  2 |     1.5 |     0
+  3 |  1.4999 |     0
+  4 | 1.50001 |     0
+  5 |     0.1 |     0
+(5 rows)
+
+--
+-- SKIP TO / Backtracking / Frame boundary
+--
 -- match everything
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
  FROM stock
@@ -1128,6 +1311,47 @@ DOWN AS price < PREV(price)
  company2 | 07-10-2023 |  1300 |             |            |      |      |      |                       |     0
 (20 rows)
 
+-- row_number() within RPR reduced frame
+SELECT company, tdate, price, row_number() OVER w, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (START UP+ DOWN+)
+ DEFINE
+  START AS TRUE,
+  UP AS price > PREV(price),
+  DOWN AS price < PREV(price)
+);
+ company  |   tdate    | price | row_number | count 
+----------+------------+-------+------------+-------
+ company1 | 07-01-2023 |   100 |          1 |     4
+ company1 | 07-02-2023 |   200 |          2 |     0
+ company1 | 07-03-2023 |   150 |          3 |     0
+ company1 | 07-04-2023 |   140 |          4 |     0
+ company1 | 07-05-2023 |   150 |          5 |     0
+ company1 | 07-06-2023 |    90 |          6 |     4
+ company1 | 07-07-2023 |   110 |          7 |     0
+ company1 | 07-08-2023 |   130 |          8 |     0
+ company1 | 07-09-2023 |   120 |          9 |     0
+ company1 | 07-10-2023 |   130 |         10 |     0
+ company2 | 07-01-2023 |    50 |          1 |     4
+ company2 | 07-02-2023 |  2000 |          2 |     0
+ company2 | 07-03-2023 |  1500 |          3 |     0
+ company2 | 07-04-2023 |  1400 |          4 |     0
+ company2 | 07-05-2023 |  1500 |          5 |     0
+ company2 | 07-06-2023 |    60 |          6 |     4
+ company2 | 07-07-2023 |  1100 |          7 |     0
+ company2 | 07-08-2023 |  1300 |          8 |     0
+ company2 | 07-09-2023 |  1200 |          9 |     0
+ company2 | 07-10-2023 |  1300 |         10 |     0
+(20 rows)
+
+--
+-- SQL Integration: JOIN, CTE, LATERAL
+--
 -- JOIN case
 CREATE TEMP TABLE t1 (i int, v1 int);
 CREATE TEMP TABLE t2 (j int, v2 int);
@@ -1262,6 +1486,9 @@ SELECT id, i, j, count(*) OVER w
   1 | 10 | 20 |     0
 (10 rows)
 
+--
+-- Large-scale / scalability tests
+--
 -- Smoke test for larger partitions.
 WITH s AS (
  SELECT v, count(*) OVER w AS c
@@ -1329,7 +1556,7 @@ SELECT match_first, match_last, match_len FROM result WHERE match_len > 0;
 (1 row)
 
 --
--- Using IGNORE NULLS
+-- IGNORE NULLS
 --
 -- no NULL rows case. The result should be identical with "basic test using PREV"
 SELECT company, tdate, price, first_value(price) IGNORE NULLS OVER w,
@@ -1371,7 +1598,7 @@ SELECT company, tdate, price, first_value(price) IGNORE NULLS OVER w,
 (20 rows)
 
 -- nth_value with IGNORE NULLS option wants to find the second row but
--- due a NULL in the midlle, it returns the third row.
+-- due to a NULL in the middle, it returns the third row.
 WITH data AS (
  SELECT * FROM (VALUES
   (10, 1), (11, NULL), (12, 3), (13, 4)
@@ -1395,8 +1622,8 @@ WITH data AS (
 (4 rows)
 
 -- nth_value with IGNORE NULLS option wants to find the third row but
--- due a NULL in the midlle, it reaches the end of reduced frame and
--- return NULL
+-- due to a NULL in the middle, it reaches the end of reduced frame and
+-- returns NULL
 WITH data AS (
  SELECT * FROM (VALUES
   (10, 1), (11, NULL), (12, 3), (13, 4)
@@ -1459,2555 +1686,156 @@ WINDOW w AS (
  company2 | 07-10-2023 |  1300 |         
 (20 rows)
 
--- View and pg_get_viewdef tests.
-CREATE TEMP VIEW v_window AS
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w,
- nth_value(tdate, 2) OVER w AS nth_second
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- IGNORE NULLS + first_value where first value in reduced frame is NULL
+WITH data AS (
+ SELECT * FROM (VALUES
+  (1, NULL), (2, NULL), (3, 30), (4, 40)
+ ) AS t(id, val))
+SELECT id, val,
+ first_value(val) IGNORE NULLS OVER w AS fv_ignull,
+ count(*) OVER w
+FROM data
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-SELECT * FROM v_window;
- company  |   tdate    | price | first_value | last_value | nth_second 
-----------+------------+-------+-------------+------------+------------
- company1 | 07-01-2023 |   100 |         100 |        140 | 07-02-2023
- company1 | 07-02-2023 |   200 |             |            | 
- company1 | 07-03-2023 |   150 |             |            | 
- company1 | 07-04-2023 |   140 |             |            | 
- company1 | 07-05-2023 |   150 |             |            | 
- company1 | 07-06-2023 |    90 |          90 |        120 | 07-07-2023
- company1 | 07-07-2023 |   110 |             |            | 
- company1 | 07-08-2023 |   130 |             |            | 
- company1 | 07-09-2023 |   120 |             |            | 
- company1 | 07-10-2023 |   130 |             |            | 
- company2 | 07-01-2023 |    50 |          50 |       1400 | 07-02-2023
- company2 | 07-02-2023 |  2000 |             |            | 
- company2 | 07-03-2023 |  1500 |             |            | 
- company2 | 07-04-2023 |  1400 |             |            | 
- company2 | 07-05-2023 |  1500 |             |            | 
- company2 | 07-06-2023 |    60 |          60 |       1200 | 07-07-2023
- company2 | 07-07-2023 |  1100 |             |            | 
- company2 | 07-08-2023 |  1300 |             |            | 
- company2 | 07-09-2023 |  1200 |             |            | 
- company2 | 07-10-2023 |  1300 |             |            | 
-(20 rows)
-
-SELECT pg_get_viewdef('v_window');
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     first_value(price) OVER w AS first_value,                                        +
-     last_value(price) OVER w AS last_value,                                          +
-     nth_value(tdate, 2) OVER w AS nth_second                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (start up+ down+)                                                          +
-   DEFINE                                                                             +
-   start AS true,                                                                     +
-   up AS (price > prev(price)),                                                       +
-   down AS (price < prev(price)) );
-(1 row)
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+)
+ DEFINE A AS TRUE
+);
+ id | val | fv_ignull | count 
+----+-----+-----------+-------
+  1 |     |        30 |     4
+  2 |     |           |     0
+  3 |  30 |           |     0
+  4 |  40 |           |     0
+(4 rows)
 
---
--- Pattern optimization tests
--- VIEW shows original pattern, EXPLAIN shows optimized pattern
---
--- Test: duplicate alternatives removal (A | B | A)+ -> (A | B)+
-CREATE TEMP VIEW v_opt_dup AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- IGNORE NULLS + all values NULL in reduced frame
+WITH data AS (
+ SELECT * FROM (VALUES
+  (1, NULL), (2, NULL), (3, NULL)
+ ) AS t(id, val))
+SELECT id, val,
+ first_value(val) IGNORE NULLS OVER w AS fv_ignull,
+ last_value(val) IGNORE NULLS OVER w AS lv_ignull,
+ count(*) OVER w
+FROM data
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | B | A)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+)
+ DEFINE A AS TRUE
 );
-SELECT pg_get_viewdef('v_opt_dup');  -- original: ((a | b | a)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a | b | a)+)                                                             +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
+ id | val | fv_ignull | lv_ignull | count 
+----+-----+-----------+-----------+-------
+  1 |     |           |           |     3
+  2 |     |           |           |     0
+  3 |     |           |           |     0
+(3 rows)
 
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_dup;  -- optimized: ((a | b)+)
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_dup
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a | b)+
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
+--
+-- last_value IGNORE NULLS with reduced frame containing all NULLs
+-- Exercises ignorenulls_getfuncarginframe SEEK_TAIL out-of-frame path
+-- when notnull_relpos >= num_reduced_frame.
+--
+CREATE TEMP TABLE rpr_nullval (id INT, val INT);
+INSERT INTO rpr_nullval VALUES (1, 10), (2, NULL), (3, NULL), (4, 20);
+SELECT id, val,
+       last_value(val) IGNORE NULLS OVER w AS lv_ignull,
+       count(*) OVER w AS cnt
+FROM rpr_nullval
+WINDOW w AS (
+  ORDER BY id
+  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  AFTER MATCH SKIP PAST LAST ROW
+  PATTERN (A B+)
+  DEFINE
+    A AS val IS NOT NULL,
+    B AS val IS NULL
+);
+ id | val | lv_ignull | cnt 
+----+-----+-----------+-----
+  1 |  10 |        10 |   3
+  2 |     |           |   0
+  3 |     |           |   0
+  4 |  20 |           |   0
+(4 rows)
 
--- Test: duplicate group removal ((A | B)+ | (A | B)+) -> (A | B)+
-CREATE TEMP VIEW v_opt_dup_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | B)+ | (A | B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
+--
+-- NULL handling
+--
+CREATE TEMP TABLE stock_null (company TEXT, tdate DATE, price INTEGER);
+INSERT INTO stock_null VALUES ('c1', '2023-07-01', 100);
+INSERT INTO stock_null VALUES ('c1', '2023-07-02', NULL);  -- NULL in middle
+INSERT INTO stock_null VALUES ('c1', '2023-07-03', 200);
+INSERT INTO stock_null VALUES ('c1', '2023-07-04', 150);
+SELECT company, tdate, price, count(*) OVER w AS match_count
+FROM stock_null
+WINDOW w AS (
+  PARTITION BY company
+  ORDER BY tdate
+  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  PATTERN (START UP DOWN)
+  DEFINE START AS TRUE, UP AS price > PREV(price), DOWN AS price <
+PREV(price)
 );
-SELECT pg_get_viewdef('v_opt_dup_group');  -- original: ((a | b)+ | (a | b)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a | b)+ | (a | b)+)                                                      +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_dup_group;  -- optimized: ((a | b)+)
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_dup_group
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a | b)+
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
+ company |   tdate    | price | match_count 
+---------+------------+-------+-------------
+ c1      | 07-01-2023 |   100 |           0
+ c1      | 07-02-2023 |       |           0
+ c1      | 07-03-2023 |   200 |           0
+ c1      | 07-04-2023 |   150 |           0
+(4 rows)
 
--- Test: consecutive vars merge (A A A) -> A{3}
-CREATE TEMP VIEW v_opt_merge AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- Consecutive NULLs: PREV navigates through NULL values
+CREATE TEMP TABLE rpr_consec_null (id INT, val INT);
+INSERT INTO rpr_consec_null VALUES
+ (1, 100), (2, NULL), (3, NULL), (4, NULL), (5, 200), (6, 300);
+-- PREV(val) IS NULL succeeds for both null_slot (first row) and actual NULL
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_consec_null
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A A)
- DEFINE
-  A AS price >= 140 AND price <= 150
-);
-SELECT pg_get_viewdef('v_opt_merge');  -- original: (a a a)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a a a)                                                                    +
-   DEFINE                                                                             +
-   a AS ((price >= 140) AND (price <= 150)) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge;  -- optimized: a{3}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{3}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+ C)
+ DEFINE
+  A AS val IS NULL,
+  B AS val IS NULL AND PREV(val) IS NULL,
+  C AS val IS NOT NULL
+);
+ id | val | cnt 
+----+-----+-----
+  1 | 100 |   0
+  2 |     |   4
+  3 |     |   0
+  4 |     |   0
+  5 | 200 |   0
+  6 | 300 |   0
+(6 rows)
 
--- Test: quantified vars merge (A A+ A) -> A{3,}
-CREATE TEMP VIEW v_opt_merge_quant AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- NEXT(val) through consecutive NULLs
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_consec_null
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A+ A)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_quant');  -- original: (a a+ a)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a a+ a)                                                                   +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_quant;  -- optimized: a{3,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_quant
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{3,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: merge two unbounded (A+ A+) -> A{2,}
-CREATE TEMP VIEW v_opt_merge_unbounded AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A+ A+)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_unbounded');  -- original: (a+ a+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a+ a+)                                                                    +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_unbounded;  -- optimized: a{2,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_unbounded
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{2,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: merge with zero-min (A* A+) -> A+
-CREATE TEMP VIEW v_opt_merge_star AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A* A+)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_star');  -- original: (a* a+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a* a+)                                                                    +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_star;  -- optimized: a+
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_star
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a+"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: complex merge (A A{2} A+ A{3}) -> A{7,}
-CREATE TEMP VIEW v_opt_merge_complex AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A{2} A+ A{3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_complex');  -- original: (a a{2} a+ a{3})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a a{2} a+ a{3})                                                           +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_complex;  -- optimized: a{7,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_complex
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{7,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: group merge ((A B) (A B)+) -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B) (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group');  -- original: ((a b) (a b)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a b) (a b)+)                                                             +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group;  -- expected: (a b){2,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_group
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){2,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: group merge A B (A B)+ -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_group2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group2');  -- original: (a b (a b)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a b (a b)+)                                                               +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group2;  -- expected: (a b){2,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_group2
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){2,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: group merge (A B) (A B)+ (A B) -> (A B){3,}
-CREATE TEMP VIEW v_opt_merge_group3 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B) (A B)+ (A B))
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group3');  -- original: ((a b) (a b)+ (a b))
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a b) (a b)+ (a b))                                                       +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group3;  -- expected: (a b){3,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_group3
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){3,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: group merge A B A B (A B)+ A B A B -> (A B){5,}
-CREATE TEMP VIEW v_opt_merge_group4 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B A B (A B)+ A B A B)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group4');  -- original: (a b a b (a b)+ a b a b)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a b a b (a b)+ a b a b)                                                   +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group4;  -- expected: (a b){5,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_group4
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){5,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: group merge C A B (A B)+ A B C -> C (A B){3,} C
-CREATE TEMP VIEW v_opt_merge_group5 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (C A B (A B)+ A B C)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100,
-  C AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_merge_group5');  -- original: (c a b (a b)+ a b c)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (c a b (a b)+ a b c)                                                       +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100),                                                               +
-   c AS (price > 200) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group5;  -- expected: c (a b){3,} c
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_group5
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: c (a b){3,} c
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: consecutive GROUP merge (A B)+ (A B)+ -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_consec_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B)+ (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_consec_group');  -- original: ((a b)+ (a b)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a b)+ (a b)+)                                                            +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_consec_group;  -- expected: (a b){2,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_consec_group
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){2,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: consecutive GROUP merge with different quantifiers (A B){2} (A B){3} -> (A B){5}
-CREATE TEMP VIEW v_opt_merge_consec_group2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B){2} (A B){3})
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_consec_group2');  -- original: ((a b){2} (a b){3})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a b){2} (a b){3})                                                        +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_consec_group2;  -- expected: (a b){5}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_merge_consec_group2
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a b){5}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test {n} quantifier display
-CREATE TEMP VIEW v_quantifier_n AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A{3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_quantifier_n');
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a{3})                                                                     +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
--- Test {n,} quantifier display
-CREATE TEMP VIEW v_quantifier_n_plus AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A{2,})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_quantifier_n_plus');
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a{2,})                                                                    +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
--- Test: flatten nested SEQ (A (B C)) -> A B C
-CREATE TEMP VIEW v_opt_flatten_seq AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A (B C))
- DEFINE
-  A AS price > 100,
-  B AS price > 150,
-  C AS price < 150
-);
-SELECT pg_get_viewdef('v_opt_flatten_seq');  -- original: (a (b c))
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a (b c))                                                                  +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price > 150),                                                                +
-   c AS (price < 150) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_flatten_seq;  -- optimized: a b c
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_flatten_seq
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a b c
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: flatten nested ALT (A | (B | C)) -> (A | B | C)
-CREATE TEMP VIEW v_opt_flatten_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | (B | C))+)
- DEFINE
-  A AS price > 200,
-  B AS price > 100,
-  C AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_flatten_alt');  -- original: ((a | (b | c))+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a | (b | c))+)                                                           +
-   DEFINE                                                                             +
-   a AS (price > 200),                                                                +
-   b AS (price > 100),                                                                +
-   c AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_flatten_alt;  -- optimized: ((a | b | c))+
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_flatten_alt
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a | b | c)+
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: unwrap GROUP{1,1} ((A)) -> A
-CREATE TEMP VIEW v_opt_unwrap_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A)))
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_unwrap_group');  -- original: (((a)))
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (((a)))                                                                    +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_unwrap_group;  -- optimized: a
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_unwrap_group
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: quantifier multiplication (A{2}){3} -> A{6}
-CREATE TEMP VIEW v_opt_quant_mult AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2}){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult');  -- original: ((a{2}){3})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a{2}){3})                                                                +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult;  -- optimized: a{6}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_quant_mult
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{6}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: quantifier multiplication (A{2,4}){3} -> A{6,12}
-CREATE TEMP VIEW v_opt_quant_mult_range AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2,4}){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_range');  -- original: ((a{2,4}){3})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a{2,4}){3})                                                              +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_range;  -- optimized: a{6,12}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_quant_mult_range
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a{6,12}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: quantifier multiplication blocked (A{2}){3,5} -> no change
--- outer range with child exact > 1 causes gaps (6,8,10 not 6,7,8,9,10)
-CREATE TEMP VIEW v_opt_quant_mult_range2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2}){3,5})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_range2');  -- original: ((a{2}){3,5})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a{2}){3,5})                                                              +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_range2;  -- NOT optimized: (a{2}){3,5}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_quant_mult_range2
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a{2}){3,5}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: quantifier multiplication blocked by INF (A+){3} -> no change
-CREATE TEMP VIEW v_opt_quant_mult_inf AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A+){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_inf');  -- original: ((a+){3})
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a+){3})                                                                  +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_inf;  -- no multiply: (a+){3}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_quant_mult_inf
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a+"){3}
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: unwrap single-item ALT after duplicate removal (A | A) -> A
-CREATE TEMP VIEW v_opt_unwrap_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | A)+)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_unwrap_alt');  -- original: ((a | a)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN ((a | a)+)                                                                 +
-   DEFINE                                                                             +
-   a AS (price > 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_unwrap_alt;  -- optimized: a+
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_unwrap_alt
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a+"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: GROUP{1,1} to SEQ with flatten ((A B)(C D)) -> A B C D
-CREATE TEMP VIEW v_opt_group_to_seq AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A B)(C D)))
- DEFINE
-  A AS price > 200,
-  B AS price > 150,
-  C AS price > 100,
-  D AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_group_to_seq');  -- original: (((a b)(c d)))
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (((a b) (c d)))                                                            +
-   DEFINE                                                                             +
-   a AS (price > 200),                                                                +
-   b AS (price > 150),                                                                +
-   c AS (price > 100),                                                                +
-   d AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_group_to_seq;  -- optimized: a b c d
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_group_to_seq
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: a b c d
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: combined consecutive GROUP + prefix merge A B (A B)+ (A B)+ -> (A B){3,}
-CREATE TEMP VIEW v_opt_combined_merge AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B (A B)+ (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_combined_merge');  -- original: (a b (a b)+ (a b)+)
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (a b (a b)+ (a b)+)                                                        +
-   DEFINE                                                                             +
-   a AS (price > 100),                                                                +
-   b AS (price <= 100) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_combined_merge;  -- expected: (a b){3,}
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_combined_merge
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: (a' b'){3,}"
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: nested ALT pattern - bug reproduction
-CREATE TEMP VIEW v_opt_nested_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A B) | C) D | A B C)
- DEFINE
-  A AS price <= 100,
-  B AS price <= 150,
-  C AS price <= 200,
-  D AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_nested_alt');
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (((a b) | c) d | a b c)                                                    +
-   DEFINE                                                                             +
-   a AS (price <= 100),                                                               +
-   b AS (price <= 150),                                                               +
-   c AS (price <= 200),                                                               +
-   d AS (price > 200) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_nested_alt;
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_nested_alt
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: ((a b | c) d | a b c)
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
--- Test: nested ALT with unbounded - A+ inside
-CREATE TEMP VIEW v_opt_nested_alt2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A+ B) | C) D | A B C)
- DEFINE
-  A AS price <= 100,
-  B AS price <= 150,
-  C AS price <= 200,
-  D AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_nested_alt2');
-                                    pg_get_viewdef                                     
----------------------------------------------------------------------------------------
-  SELECT company,                                                                     +
-     tdate,                                                                           +
-     price,                                                                           +
-     count(*) OVER w AS count                                                         +
-    FROM stock                                                                        +
-   WINDOW w AS (PARTITION BY company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
-   AFTER MATCH SKIP PAST LAST ROW                                                     +
-   INITIAL                                                                            +
-   PATTERN (((a+ b) | c) d | a b c)                                                   +
-   DEFINE                                                                             +
-   a AS (price <= 100),                                                               +
-   b AS (price <= 150),                                                               +
-   c AS (price <= 200),                                                               +
-   d AS (price > 200) );
-(1 row)
-
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_nested_alt2;
-                                             QUERY PLAN                                             
-----------------------------------------------------------------------------------------------------
- Subquery Scan on v_opt_nested_alt2
-   ->  WindowAgg
-         Window: w AS (PARTITION BY stock.company ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-         Pattern: ((a+" b | c) d | a b c)
-         ->  Sort
-               Sort Key: stock.company
-               ->  Seq Scan on stock
-(7 rows)
-
---
--- Error cases
---
--- row pattern definition variable name must not appear more than once
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price),
-  UP AS price > PREV(price)
-);
-ERROR:  row pattern definition variable name "up" appears more than once in DEFINE clause
-LINE 11:   UP AS price > PREV(price),
-           ^
--- subqueries in DEFINE clause are not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START LOWPRICE)
- DEFINE
-  START AS TRUE,
-  LOWPRICE AS price < (SELECT 100)
-);
-ERROR:  cannot use subquery in DEFINE expression
-LINE 11:   LOWPRICE AS price < (SELECT 100)
-                               ^
--- aggregates in DEFINE clause are not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START LOWPRICE)
- DEFINE
-  START AS TRUE,
-  LOWPRICE AS price < count(*)
-);
-ERROR:  aggregate functions are not allowed in DEFINE
-LINE 11:   LOWPRICE AS price < count(*)
-                               ^
--- FRAME must start at current row when row pattern recognition is used
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-ERROR:  FRAME must start at CURRENT ROW when row pattern recognition is used
-LINE 6:  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
-         ^
-DETAIL:  Current frame starts with UNBOUNDED PRECEDING.
-HINT:  Use: ROWS BETWEEN CURRENT ROW AND ...
--- EXCLUDE is not permitted
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- EXCLUDE CURRENT ROW
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-ERROR:  EXCLUDE options are not permitted when row pattern recognition is used
-LINE 7:  EXCLUDE CURRENT ROW
-         ^
-DETAIL:  Frame definition includes EXCLUDE CURRENT ROW.
-HINT:  Remove the EXCLUDE clause from the window definition.
--- SEEK is not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- SEEK
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-ERROR:  SEEK is not supported
-LINE 8:  SEEK
-         ^
-HINT:  Use INITIAL instead.
--- PREV's argument must have at least 1 column reference
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-ERROR:  row pattern navigation operation's argument must include at least one column reference
--- Unsupported quantifier
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP~ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-ERROR:  unsupported quantifier "~"
-LINE 9:  PATTERN (START UP~ DOWN+)
-                          ^
-HINT:  Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions.
--- PREV(1) missing column reference (error)
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP+? DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-ERROR:  row pattern navigation operation's argument must include at least one column reference
--- Maximum pattern variables is 251 (RPR_VARID_MAX)
--- Error: 252 variables exceeds limit of 251
-DO $$
-DECLARE
-    pattern_vars text;
-    define_vars text;
-    query text;
-BEGIN
-    SELECT string_agg('v' || lpad(i::text, 3, '0'), ' '),
-           string_agg('v' || lpad(i::text, 3, '0') || ' AS TRUE', ', ')
-    INTO pattern_vars, define_vars
-    FROM generate_series(1, 252) i;
-
-    query := format('SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (%s)
-        DEFINE %s)', pattern_vars, define_vars);
-
-    EXECUTE query;
-END;
-$$;
-ERROR:  too many pattern variables
-DETAIL:  Maximum is 251.
-CONTEXT:  SQL statement "SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (v001 v002 v003 v004 v005 v006 v007 v008 v009 v010 v011 v012 v013 v014 v015 v016 v017 v018 v019 v020 v021 v022 v023 v024 v025 v026 v027 v028 v029 v030 v031 v032 v033 v034 v035 v036 v037 v038 v039 v040 v041 v042 v043 v044 v045 v046 v047 v048 v049 v050 v051 v052 v053 v054 v055 v056 v057 v058 v059 v060 v061 v062 v063 v064 v065 v066 v067 v068 v069 v070 v071 v072 v073 v074 v075 v076 v077 v078 v079 v080 v081 v082 v083 v084 v085 v086 v087 v088 v089 v090 v091 v092 v093 v094 v095 v096 v097 v098 v099 v100 v101 v102 v103 v104 v105 v106 v107 v108 v109 v110 v111 v112 v113 v114 v115 v116 v117 v118 v119 v120 v121 v122 v123 v124 v125 v126 v127 v128 v129 v130 v131 v132 v133 v134 v135 v136 v137 v138 v139 v140 v141 v142 v143 v144 v145 v146 v147 v148 v149 v150 v151 v152 v153 v154 v155 v156 v157 v158 v159 v160 v161 v162 v163 v164 v165 v166 v167 v168 v169 v170 v171 v172 v173 v174 v175 v176 v177 v178 v179 v180 v181 v182 v183 v184 v185 v186 v187 v188 v189 v190 v191 v192 v193 v194 v195 v196 v197 v198 v199 v200 v201 v202 v203 v204 v205 v206 v207 v208 v209 v210 v211 v212 v213 v214 v215 v216 v217 v218 v219 v220 v221 v222 v223 v224 v225 v226 v227 v228 v229 v230 v231 v232 v233 v234 v235 v236 v237 v238 v239 v240 v241 v242 v243 v244 v245 v246 v247 v248 v249 v250 v251 v252)
-        DEFINE v001 AS TRUE, v002 AS TRUE, v003 AS TRUE, v004 AS TRUE, v005 AS TRUE, v006 AS TRUE, v007 AS TRUE, v008 AS TRUE, v009 AS TRUE, v010 AS TRUE, v011 AS TRUE, v012 AS TRUE, v013 AS TRUE, v014 AS TRUE, v015 AS TRUE, v016 AS TRUE, v017 AS TRUE, v018 AS TRUE, v019 AS TRUE, v020 AS TRUE, v021 AS TRUE, v022 AS TRUE, v023 AS TRUE, v024 AS TRUE, v025 AS TRUE, v026 AS TRUE, v027 AS TRUE, v028 AS TRUE, v029 AS TRUE, v030 AS TRUE, v031 AS TRUE, v032 AS TRUE, v033 AS TRUE, v034 AS TRUE, v035 AS TRUE, v036 AS TRUE, v037 AS TRUE, v038 AS TRUE, v039 AS TRUE, v040 AS TRUE, v041 AS TRUE, v042 AS TRUE, v043 AS TRUE, v044 AS TRUE, v045 AS TRUE, v046 AS TRUE, v047 AS TRUE, v048 AS TRUE, v049 AS TRUE, v050 AS TRUE, v051 AS TRUE, v052 AS TRUE, v053 AS TRUE, v054 AS TRUE, v055 AS TRUE, v056 AS TRUE, v057 AS TRUE, v058 AS TRUE, v059 AS TRUE, v060 AS TRUE, v061 AS TRUE, v062 AS TRUE, v063 AS TRUE, v064 AS TRUE, v065 AS TRUE, v066 AS TRUE, v067 AS TRUE, v068 AS TRUE, v069 AS TRUE, v070 AS TRUE, v071 AS TRUE, v072 AS TRUE, v073 AS TRUE, v074 AS TRUE, v075 AS TRUE, v076 AS TRUE, v077 AS TRUE, v078 AS TRUE, v079 AS TRUE, v080 AS TRUE, v081 AS TRUE, v082 AS TRUE, v083 AS TRUE, v084 AS TRUE, v085 AS TRUE, v086 AS TRUE, v087 AS TRUE, v088 AS TRUE, v089 AS TRUE, v090 AS TRUE, v091 AS TRUE, v092 AS TRUE, v093 AS TRUE, v094 AS TRUE, v095 AS TRUE, v096 AS TRUE, v097 AS TRUE, v098 AS TRUE, v099 AS TRUE, v100 AS TRUE, v101 AS TRUE, v102 AS TRUE, v103 AS TRUE, v104 AS TRUE, v105 AS TRUE, v106 AS TRUE, v107 AS TRUE, v108 AS TRUE, v109 AS TRUE, v110 AS TRUE, v111 AS TRUE, v112 AS TRUE, v113 AS TRUE, v114 AS TRUE, v115 AS TRUE, v116 AS TRUE, v117 AS TRUE, v118 AS TRUE, v119 AS TRUE, v120 AS TRUE, v121 AS TRUE, v122 AS TRUE, v123 AS TRUE, v124 AS TRUE, v125 AS TRUE, v126 AS TRUE, v127 AS TRUE, v128 AS TRUE, v129 AS TRUE, v130 AS TRUE, v131 AS TRUE, v132 AS TRUE, v133 AS TRUE, v134 AS TRUE, v135 AS TRUE, v136 AS TRUE, v137 AS TRUE, v138 AS TRUE, v139 AS TRUE, v140 AS TRUE, v141 AS TRUE, v142 AS TRUE, v143 AS TRUE, v144 AS TRUE, v145 AS TRUE, v146 AS TRUE, v147 AS TRUE, v148 AS TRUE, v149 AS TRUE, v150 AS TRUE, v151 AS TRUE, v152 AS TRUE, v153 AS TRUE, v154 AS TRUE, v155 AS TRUE, v156 AS TRUE, v157 AS TRUE, v158 AS TRUE, v159 AS TRUE, v160 AS TRUE, v161 AS TRUE, v162 AS TRUE, v163 AS TRUE, v164 AS TRUE, v165 AS TRUE, v166 AS TRUE, v167 AS TRUE, v168 AS TRUE, v169 AS TRUE, v170 AS TRUE, v171 AS TRUE, v172 AS TRUE, v173 AS TRUE, v174 AS TRUE, v175 AS TRUE, v176 AS TRUE, v177 AS TRUE, v178 AS TRUE, v179 AS TRUE, v180 AS TRUE, v181 AS TRUE, v182 AS TRUE, v183 AS TRUE, v184 AS TRUE, v185 AS TRUE, v186 AS TRUE, v187 AS TRUE, v188 AS TRUE, v189 AS TRUE, v190 AS TRUE, v191 AS TRUE, v192 AS TRUE, v193 AS TRUE, v194 AS TRUE, v195 AS TRUE, v196 AS TRUE, v197 AS TRUE, v198 AS TRUE, v199 AS TRUE, v200 AS TRUE, v201 AS TRUE, v202 AS TRUE, v203 AS TRUE, v204 AS TRUE, v205 AS TRUE, v206 AS TRUE, v207 AS TRUE, v208 AS TRUE, v209 AS TRUE, v210 AS TRUE, v211 AS TRUE, v212 AS TRUE, v213 AS TRUE, v214 AS TRUE, v215 AS TRUE, v216 AS TRUE, v217 AS TRUE, v218 AS TRUE, v219 AS TRUE, v220 AS TRUE, v221 AS TRUE, v222 AS TRUE, v223 AS TRUE, v224 AS TRUE, v225 AS TRUE, v226 AS TRUE, v227 AS TRUE, v228 AS TRUE, v229 AS TRUE, v230 AS TRUE, v231 AS TRUE, v232 AS TRUE, v233 AS TRUE, v234 AS TRUE, v235 AS TRUE, v236 AS TRUE, v237 AS TRUE, v238 AS TRUE, v239 AS TRUE, v240 AS TRUE, v241 AS TRUE, v242 AS TRUE, v243 AS TRUE, v244 AS TRUE, v245 AS TRUE, v246 AS TRUE, v247 AS TRUE, v248 AS TRUE, v249 AS TRUE, v250 AS TRUE, v251 AS TRUE, v252 AS TRUE)"
-PL/pgSQL function inline_code_block line 17 at EXECUTE
--- Error: 253 variables exceeds limit of 251
-DO $$
-DECLARE
-    pattern_vars text;
-    define_vars text;
-    query text;
-BEGIN
-    SELECT string_agg('v' || lpad(i::text, 3, '0'), ' '),
-           string_agg('v' || lpad(i::text, 3, '0') || ' AS TRUE', ', ')
-    INTO pattern_vars, define_vars
-    FROM generate_series(1, 253) i;
-
-    query := format('SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (%s)
-        DEFINE %s)', pattern_vars, define_vars);
-
-    EXECUTE query;
-END;
-$$;
-ERROR:  too many pattern variables
-DETAIL:  Maximum is 251.
-CONTEXT:  SQL statement "SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (v001 v002 v003 v004 v005 v006 v007 v008 v009 v010 v011 v012 v013 v014 v015 v016 v017 v018 v019 v020 v021 v022 v023 v024 v025 v026 v027 v028 v029 v030 v031 v032 v033 v034 v035 v036 v037 v038 v039 v040 v041 v042 v043 v044 v045 v046 v047 v048 v049 v050 v051 v052 v053 v054 v055 v056 v057 v058 v059 v060 v061 v062 v063 v064 v065 v066 v067 v068 v069 v070 v071 v072 v073 v074 v075 v076 v077 v078 v079 v080 v081 v082 v083 v084 v085 v086 v087 v088 v089 v090 v091 v092 v093 v094 v095 v096 v097 v098 v099 v100 v101 v102 v103 v104 v105 v106 v107 v108 v109 v110 v111 v112 v113 v114 v115 v116 v117 v118 v119 v120 v121 v122 v123 v124 v125 v126 v127 v128 v129 v130 v131 v132 v133 v134 v135 v136 v137 v138 v139 v140 v141 v142 v143 v144 v145 v146 v147 v148 v149 v150 v151 v152 v153 v154 v155 v156 v157 v158 v159 v160 v161 v162 v163 v164 v165 v166 v167 v168 v169 v170 v171 v172 v173 v174 v175 v176 v177 v178 v179 v180 v181 v182 v183 v184 v185 v186 v187 v188 v189 v190 v191 v192 v193 v194 v195 v196 v197 v198 v199 v200 v201 v202 v203 v204 v205 v206 v207 v208 v209 v210 v211 v212 v213 v214 v215 v216 v217 v218 v219 v220 v221 v222 v223 v224 v225 v226 v227 v228 v229 v230 v231 v232 v233 v234 v235 v236 v237 v238 v239 v240 v241 v242 v243 v244 v245 v246 v247 v248 v249 v250 v251 v252 v253)
-        DEFINE v001 AS TRUE, v002 AS TRUE, v003 AS TRUE, v004 AS TRUE, v005 AS TRUE, v006 AS TRUE, v007 AS TRUE, v008 AS TRUE, v009 AS TRUE, v010 AS TRUE, v011 AS TRUE, v012 AS TRUE, v013 AS TRUE, v014 AS TRUE, v015 AS TRUE, v016 AS TRUE, v017 AS TRUE, v018 AS TRUE, v019 AS TRUE, v020 AS TRUE, v021 AS TRUE, v022 AS TRUE, v023 AS TRUE, v024 AS TRUE, v025 AS TRUE, v026 AS TRUE, v027 AS TRUE, v028 AS TRUE, v029 AS TRUE, v030 AS TRUE, v031 AS TRUE, v032 AS TRUE, v033 AS TRUE, v034 AS TRUE, v035 AS TRUE, v036 AS TRUE, v037 AS TRUE, v038 AS TRUE, v039 AS TRUE, v040 AS TRUE, v041 AS TRUE, v042 AS TRUE, v043 AS TRUE, v044 AS TRUE, v045 AS TRUE, v046 AS TRUE, v047 AS TRUE, v048 AS TRUE, v049 AS TRUE, v050 AS TRUE, v051 AS TRUE, v052 AS TRUE, v053 AS TRUE, v054 AS TRUE, v055 AS TRUE, v056 AS TRUE, v057 AS TRUE, v058 AS TRUE, v059 AS TRUE, v060 AS TRUE, v061 AS TRUE, v062 AS TRUE, v063 AS TRUE, v064 AS TRUE, v065 AS TRUE, v066 AS TRUE, v067 AS TRUE, v068 AS TRUE, v069 AS TRUE, v070 AS TRUE, v071 AS TRUE, v072 AS TRUE, v073 AS TRUE, v074 AS TRUE, v075 AS TRUE, v076 AS TRUE, v077 AS TRUE, v078 AS TRUE, v079 AS TRUE, v080 AS TRUE, v081 AS TRUE, v082 AS TRUE, v083 AS TRUE, v084 AS TRUE, v085 AS TRUE, v086 AS TRUE, v087 AS TRUE, v088 AS TRUE, v089 AS TRUE, v090 AS TRUE, v091 AS TRUE, v092 AS TRUE, v093 AS TRUE, v094 AS TRUE, v095 AS TRUE, v096 AS TRUE, v097 AS TRUE, v098 AS TRUE, v099 AS TRUE, v100 AS TRUE, v101 AS TRUE, v102 AS TRUE, v103 AS TRUE, v104 AS TRUE, v105 AS TRUE, v106 AS TRUE, v107 AS TRUE, v108 AS TRUE, v109 AS TRUE, v110 AS TRUE, v111 AS TRUE, v112 AS TRUE, v113 AS TRUE, v114 AS TRUE, v115 AS TRUE, v116 AS TRUE, v117 AS TRUE, v118 AS TRUE, v119 AS TRUE, v120 AS TRUE, v121 AS TRUE, v122 AS TRUE, v123 AS TRUE, v124 AS TRUE, v125 AS TRUE, v126 AS TRUE, v127 AS TRUE, v128 AS TRUE, v129 AS TRUE, v130 AS TRUE, v131 AS TRUE, v132 AS TRUE, v133 AS TRUE, v134 AS TRUE, v135 AS TRUE, v136 AS TRUE, v137 AS TRUE, v138 AS TRUE, v139 AS TRUE, v140 AS TRUE, v141 AS TRUE, v142 AS TRUE, v143 AS TRUE, v144 AS TRUE, v145 AS TRUE, v146 AS TRUE, v147 AS TRUE, v148 AS TRUE, v149 AS TRUE, v150 AS TRUE, v151 AS TRUE, v152 AS TRUE, v153 AS TRUE, v154 AS TRUE, v155 AS TRUE, v156 AS TRUE, v157 AS TRUE, v158 AS TRUE, v159 AS TRUE, v160 AS TRUE, v161 AS TRUE, v162 AS TRUE, v163 AS TRUE, v164 AS TRUE, v165 AS TRUE, v166 AS TRUE, v167 AS TRUE, v168 AS TRUE, v169 AS TRUE, v170 AS TRUE, v171 AS TRUE, v172 AS TRUE, v173 AS TRUE, v174 AS TRUE, v175 AS TRUE, v176 AS TRUE, v177 AS TRUE, v178 AS TRUE, v179 AS TRUE, v180 AS TRUE, v181 AS TRUE, v182 AS TRUE, v183 AS TRUE, v184 AS TRUE, v185 AS TRUE, v186 AS TRUE, v187 AS TRUE, v188 AS TRUE, v189 AS TRUE, v190 AS TRUE, v191 AS TRUE, v192 AS TRUE, v193 AS TRUE, v194 AS TRUE, v195 AS TRUE, v196 AS TRUE, v197 AS TRUE, v198 AS TRUE, v199 AS TRUE, v200 AS TRUE, v201 AS TRUE, v202 AS TRUE, v203 AS TRUE, v204 AS TRUE, v205 AS TRUE, v206 AS TRUE, v207 AS TRUE, v208 AS TRUE, v209 AS TRUE, v210 AS TRUE, v211 AS TRUE, v212 AS TRUE, v213 AS TRUE, v214 AS TRUE, v215 AS TRUE, v216 AS TRUE, v217 AS TRUE, v218 AS TRUE, v219 AS TRUE, v220 AS TRUE, v221 AS TRUE, v222 AS TRUE, v223 AS TRUE, v224 AS TRUE, v225 AS TRUE, v226 AS TRUE, v227 AS TRUE, v228 AS TRUE, v229 AS TRUE, v230 AS TRUE, v231 AS TRUE, v232 AS TRUE, v233 AS TRUE, v234 AS TRUE, v235 AS TRUE, v236 AS TRUE, v237 AS TRUE, v238 AS TRUE, v239 AS TRUE, v240 AS TRUE, v241 AS TRUE, v242 AS TRUE, v243 AS TRUE, v244 AS TRUE, v245 AS TRUE, v246 AS TRUE, v247 AS TRUE, v248 AS TRUE, v249 AS TRUE, v250 AS TRUE, v251 AS TRUE, v252 AS TRUE, v253 AS TRUE)"
-PL/pgSQL function inline_code_block line 17 at EXECUTE
-    CREATE TEMP TABLE stock_null (company TEXT, tdate DATE, price INTEGER);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-01', 100);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-02', NULL);  -- NULL in middle
-    INSERT INTO stock_null VALUES ('c1', '2023-07-03', 200);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-04', 150);
-    SELECT company, tdate, price, count(*) OVER w AS match_count
-    FROM stock_null
-    WINDOW w AS (
-      PARTITION BY company
-      ORDER BY tdate
-      ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-      PATTERN (START UP DOWN)
-      DEFINE START AS TRUE, UP AS price > PREV(price), DOWN AS price <
-PREV(price)
-    );
- company |   tdate    | price | match_count 
----------+------------+-------+-------------
- c1      | 07-01-2023 |   100 |           0
- c1      | 07-02-2023 |       |           0
- c1      | 07-03-2023 |   200 |           0
- c1      | 07-04-2023 |   150 |           0
-(4 rows)
-
--- Overlapping match tests (requires multi-context for correct behavior)
--- Using array flags: 'X' = ANY(flags) for multi-TRUE support
--- Test 1: A B C D E | B C D | C D E F - three overlapping patterns
--- Different end points: B C D (4), A B C D E (5), C D E F (6)
-WITH test_overlap1 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E']),
-        (6, ARRAY['F'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | B C D | C D E F)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags),
-        F AS 'F' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         5
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-  6 | {F}   |             |          
-(6 rows)
-
-WITH test_overlap1 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E']),
-        (6, ARRAY['F'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B C D E | B C D | C D E F)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags),
-        F AS 'F' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         5
-  2 | {B}   |           2 |         4
-  3 | {C}   |           3 |         6
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-  6 | {F}   |             |          
-(6 rows)
-
--- PAST LAST: only one match
--- TO NEXT ROW with multi-context: three matches
---   Row 1: A B C D E (1-5)
---   Row 2: B C D (2-4) <- ends first!
---   Row 3: C D E F (3-6) <- ends last!
--- Test 1b: Longer pattern FAILS, shorter pattern should survive
--- Pattern: A+ B C D E | B+ C
--- A+ B C D E fails (no E found in sequence)
--- B+ C matches at rows 2-3
--- Result: match 2-3 (B+ C)
-WITH test_overlap1b AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1b
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A+ B C D E | B+ C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |             |          
-  2 | {B}   |           2 |         3
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Test 2: A B+ C | B+ D - long B sequence with different endings
-WITH test_overlap2 AS (
-    SELECT * FROM (VALUES
-        (1,  ARRAY['A']),
-        (2,  ARRAY['B']),
-        (3,  ARRAY['B']),
-        (4,  ARRAY['B']),
-        (5,  ARRAY['B']),
-        (6,  ARRAY['C']),
-        (7,  ARRAY['B']),
-        (8,  ARRAY['B']),
-        (9,  ARRAY['B']),
-        (10, ARRAY['D'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap2
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B+ C | B+ D)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         6
-  2 | {B}   |             |          
-  3 | {B}   |             |          
-  4 | {B}   |             |          
-  5 | {B}   |             |          
-  6 | {C}   |             |          
-  7 | {B}   |           7 |        10
-  8 | {B}   |           8 |        10
-  9 | {B}   |           9 |        10
- 10 | {D}   |             |          
-(10 rows)
-
--- Current result (correct):
---   Row 1: A B+ C (1-6)
---   Row 7-9: B+ D (7-10, 8-10, 9-10)
--- Note: Row 2-6 cannot match B+ D because Row 6 is C, not D
--- With absorption: 8-10 and 9-10 would be absorbed by 7-10 (earlier context covers later)
--- Test 3: Greedy quantifier with late failure - A B C+ D | A B
--- Pattern expects D after C+, but E comes instead ("betrayal")
-WITH test_betrayal AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['C']),
-        (5, ARRAY['C']),
-        (6, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_betrayal
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C+ D | A B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         2
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {C}   |             |          
-  5 | {C}   |             |          
-  6 | {E}   |             |          
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+ C)
+ DEFINE
+  A AS val IS NOT NULL,
+  B AS val IS NULL AND NEXT(val) IS NULL,
+  C AS val IS NULL AND NEXT(val) IS NOT NULL
+);
+ id | val | cnt 
+----+-----+-----
+  1 | 100 |   4
+  2 |     |   0
+  3 |     |   0
+  4 |     |   0
+  5 | 200 |   0
+  6 | 300 |   0
 (6 rows)
 
--- A B C+ D fails at Row 6 (E instead of D)
--- Question: Does it fallback to A B (1-2)?
--- Test 4: Lexical Order test - A B C | A B C D E
--- SQL standard: first matching alternative wins
-WITH test_lexical AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_lexical
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C | A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         3
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-(5 rows)
-
--- SQL standard Lexical Order: A B C (1-3) wins (first alternative)
--- Test 4b: Reversed pattern order - A B C D E | A B C
-WITH test_lexical2 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_lexical2
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         5
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-(5 rows)
-
--- SQL standard Lexical Order: A B C D E (1-5) wins (first alternative)
--- Test 5: Multiple TRUE in single row (overlapping pattern variables)
--- Each row matches multiple DEFINE conditions simultaneously
-WITH test_multi_true AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','B']),        -- A and B both TRUE
-        (2, ARRAY['B','C']),        -- B and C both TRUE
-        (3, ARRAY['C','D']),        -- C and D both TRUE
-        (4, ARRAY['D','E']),        -- D and E both TRUE
-        (5, ARRAY['E','_'])         -- E only
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_multi_true
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A,B} |           1 |         5
-  2 | {B,C} |             |          
-  3 | {C,D} |             |          
-  4 | {D,E} |             |          
-  5 | {E,_} |             |          
-(5 rows)
-
--- Row 1: A=T, B=T -> matches A
--- Row 2: B=T, C=T -> matches B
--- Row 3: C=T, D=T -> matches C
--- Row 4: D=T, E=T -> matches D
--- Row 5: E=T      -> matches E
--- Result: match 1-5 (A B C D E)
--- Test 6: Diagonal pattern with multi-TRUE (shifted overlap)
-WITH test_diagonal AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['B','A']),
-        (3, ARRAY['C','B']),
-        (4, ARRAY['D','C']),
-        (5, ARRAY['_','D'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_diagonal
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B C D)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A,_} |           1 |         4
-  2 | {B,A} |           2 |         5
-  3 | {C,B} |             |          
-  4 | {D,C} |             |          
-  5 | {_,D} |             |          
-(5 rows)
-
--- Possible matches:
---   Start Row 1: A(1) B(2) C(3) D(4) -> 1-4
---   Start Row 2: A(2) B(3) C(4) D(5) -> 2-5 (because Row 2 has A too!)
--- ===================================================================
--- Context Absorption Tests
--- ===================================================================
--- Test absorption 1: Basic A+ pattern - later contexts absorbed by earlier
-WITH test_absorb_basic AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['A']),
-        (5, ARRAY['B'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_basic
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+)
-    DEFINE A AS 'A' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {A}   |           2 |         4
-  3 | {A}   |           3 |         4
-  4 | {A}   |           4 |         4
-  5 | {B}   |             |          
-(5 rows)
-
--- Pattern A+ is absorbable (unbounded first element, only one unbounded)
--- 4 matches: (1-4, 2-4, 3-4, 4-4)
--- Test absorption 2: A+ B pattern - absorption with fixed suffix
-WITH test_absorb_suffix AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_suffix
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+ B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {A}   |           2 |         4
-  3 | {A}   |           3 |         4
-  4 | {B}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Pattern A+ B is absorbable (A+ unbounded first, B bounded suffix)
--- All potential matches end at same row (row 4 with B)
--- 3 matches: (1-4, 2-4, 3-4)
--- Test absorption 3: Per-branch absorption with ALT (B+ C | B+ D)
-WITH test_absorb_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['B']),
-        (2, ARRAY['B']),
-        (3, ARRAY['B']),
-        (4, ARRAY['D']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (B+ C | B+ D)
-    DEFINE
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {B}   |           1 |         4
-  2 | {B}   |           2 |         4
-  3 | {B}   |           3 |         4
-  4 | {D}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Both branches B+ C and B+ D are absorbable (B+ unbounded first)
--- B+ D branch matches: 3 matches (1-4, 2-4, 3-4)
--- Test absorption 4: Non-absorbable pattern (A B+ - unbounded not first)
-WITH test_no_absorb AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['B']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_no_absorb
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {B}   |             |          
-  3 | {B}   |             |          
-  4 | {B}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Pattern A B+ is NOT absorbable (A bounded first, B+ unbounded but not first)
--- Only Row 1 can start match (only row with A), so only one match: 1-4
--- Test absorption 5: GROUP merge enables absorption
-WITH test_absorb_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['A']),
-        (6, ARRAY['B']),
-        (7, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN ((A B) (A B)+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         6
-  2 | {B}   |             |          
-  3 | {A}   |           3 |         6
-  4 | {B}   |             |          
-  5 | {A}   |             |          
-  6 | {B}   |             |          
-  7 | {X}   |             |          
-(7 rows)
-
--- Pattern optimized: (A B) (A B)+ -> (A B){2,}
--- 2 matches: 1-6 (3 reps), 3-6 (2 reps)
--- Test absorption 6: Multiple unbounded - first element unbounded enables absorption
-WITH test_multi_unbounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['B']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_multi_unbounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+ B+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {A}   |           2 |         4
-  3 | {B}   |             |          
-  4 | {B}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- 2 matches: 1-4, 2-4 (same endpoint 4)
--- ============================================
--- Jacob's RPR Patterns (from jacob branch)
--- ============================================
--- Test: A? (optional, greedy)
-WITH jacob_optional AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_optional
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A?)
-    DEFINE A AS 'A' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         1
-  2 | {X}   |             |          
-(2 rows)
-
--- Expected: 1-1 (matches A)
--- Test: A{2} (exact count)
-WITH jacob_exact AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_exact
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{2})
-    DEFINE A AS 'A' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         2
-  2 | {A}   |             |          
-  3 | {A}   |             |          
-  4 | {X}   |             |          
-(4 rows)
-
--- Expected: 1-2
--- Test: A{1,3} (bounded range, greedy)
-WITH jacob_bounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['A']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_bounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{1,3})
-    DEFINE A AS 'A' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         3
-  2 | {A}   |             |          
-  3 | {A}   |             |          
-  4 | {A}   |           4 |         4
-  5 | {X}   |             |          
-(5 rows)
-
--- Expected: 1-3 (greedy takes max), then 4-4
--- Test: A | B (simple alternation)
-WITH jacob_simple_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_simple_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A | B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         1
-  2 | {B}   |           2 |         2
-  3 | {X}   |             |          
-(3 rows)
-
--- Expected: 1-1 (A), 2-2 (B)
--- Test: A | B | C (three-way alternation)
-WITH jacob_three_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['B']),
-        (2, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_three_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A | B | C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {B}   |           1 |         1
-  2 | {X}   |             |          
-(2 rows)
-
--- Expected: 1-1 (B)
--- Test: A B C (concatenation)
-WITH jacob_concat AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_concat
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         3
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {X}   |             |          
-(4 rows)
-
--- Expected: 1-3
--- Test: A B? C (optional middle)
-WITH jacob_optional_mid AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['C']),
-        (3, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_optional_mid
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B? C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         2
-  2 | {C}   |             |          
-  3 | {X}   |             |          
-(3 rows)
-
--- Expected: 1-2 (A C, B skipped)
--- Test: (A B){2} (nested group with quantifier)
-WITH jacob_nested_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_nested_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A B){2})
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {B}   |             |          
-  3 | {A}   |             |          
-  4 | {B}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Expected: 1-4 (A B A B)
--- Test: (A){3} (quantifier on grouped single element)
-WITH jacob_group_quant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_group_quant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A){3})
-    DEFINE A AS 'A' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         3
-  2 | {A}   |             |          
-  3 | {A}   |             |          
-  4 | {X}   |             |          
-(4 rows)
-
--- Expected: 1-3
--- Test: A B C | A B C D E (lexical order - first alt wins)
-WITH jacob_lex_first AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_lex_first
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C | A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         3
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-(5 rows)
-
--- Expected: 1-3 (A B C wins by lexical order)
--- Test: A B C D E | A B C (lexical order - longer first wins)
-WITH jacob_lex_long AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_lex_long
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         5
-  2 | {B}   |             |          
-  3 | {C}   |             |          
-  4 | {D}   |             |          
-  5 | {E}   |             |          
-(5 rows)
-
--- Expected: 1-5 (A B C D E wins by lexical order)
--- ============================================
--- Alternation with quantifiers (BUG cases from Jacob's tests)
--- ============================================
--- Test: (A | B)+ C - alternation inside quantified group followed by C
-WITH jacob_alt_quant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['C'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_alt_quant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A | B)+ C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {B}   |             |          
-  3 | {A}   |             |          
-  4 | {C}   |             |          
-(4 rows)
-
--- Expected: 1-4 (A B A C)
--- Test: ((A | B) C)+ - alternation inside group with outer quantifier
-WITH jacob_alt_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['C']),
-        (3, ARRAY['B']),
-        (4, ARRAY['C']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_alt_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (((A | B) C)+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A}   |           1 |         4
-  2 | {C}   |             |          
-  3 | {B}   |             |          
-  4 | {C}   |             |          
-  5 | {X}   |             |          
-(5 rows)
-
--- Expected: 1-4 (A C B C)
--- ============================================
--- RELUCTANT quantifiers
--- ============================================
--- Test: A+? B (reluctant) - B available at row 3, A exits early
-WITH jacob_reluctant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['A','_']),
-        (3, ARRAY['A','B']),
-        (4, ARRAY['B','_'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_reluctant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A+? B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A,_} |           1 |         3
-  2 | {A,_} |             |          
-  3 | {A,B} |             |          
-  4 | {B,_} |             |          
-(4 rows)
-
--- Test: A{1,3}? B (reluctant bounded) - same data, bounded quantifier
-WITH jacob_reluctant_bounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['A','_']),
-        (3, ARRAY['A','B']),
-        (4, ARRAY['B','_'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_reluctant_bounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{1,3}? B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
- id | flags | match_start | match_end 
-----+-------+-------------+-----------
-  1 | {A,_} |           1 |         3
-  2 | {A,_} |             |          
-  3 | {A,B} |             |          
-  4 | {B,_} |             |          
-(4 rows)
-
--- ============================================
--- Nested quantifiers (pathological patterns)
--- ============================================
--- These patterns previously caused segfault or infinite loop.
--- Now they are either optimized at compile time or handled safely at runtime.
--- Test: (A*)* - nested unbounded quantifiers (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A*)*)
-    DEFINE A AS TRUE
-);
- v | c 
----+---
- 1 | 5
- 2 | 0
- 3 | 0
- 4 | 0
- 5 | 0
-(5 rows)
-
--- Test: (A*)+ - inner nullable, outer requires one (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A*)+)
-    DEFINE A AS TRUE
-);
- v | c 
----+---
- 1 | 5
- 2 | 0
- 3 | 0
- 4 | 0
- 5 | 0
-(5 rows)
-
--- Test: (A+)* - outer nullable (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A+)*)
-    DEFINE A AS TRUE
-);
- v | c 
----+---
- 1 | 5
- 2 | 0
- 3 | 0
- 4 | 0
- 5 | 0
-(5 rows)
-
--- Test: (A+)+ - both require match (optimized to A+)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A+)+)
-    DEFINE A AS TRUE
-);
- v | c 
----+---
- 1 | 5
- 2 | 0
- 3 | 0
- 4 | 0
- 5 | 0
-(5 rows)
-
--- Test: (A* B*)* - complex nested pattern (runtime protection)
--- Not optimized but handled safely by empty-match loop prevention
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A* B*)*)
-    DEFINE A AS TRUE, B AS TRUE
-);
- v | c 
----+---
- 1 | 5
- 2 | 0
- 3 | 0
- 4 | 0
- 5 | 0
-(5 rows)
-
--- Test: (((A)*)*)*  - triple nested (optimized through recursive optimization)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 3) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((((A)*)*)*)
-    DEFINE A AS TRUE
-);
- v | c 
----+---
- 1 | 3
- 2 | 0
- 3 | 0
-(3 rows)
-
+DROP TABLE rpr_consec_null;
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index ab878443379..3383b242ef0 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -1449,7 +1449,7 @@ ERROR:  syntax error at or near "*"
 LINE 6:     PATTERN (A+ *)
                         ^
 -- Expected: ERROR: syntax error at or near "*"
--- ? ? (invalid combination)
+-- ? ? (parsed as ?? reluctant quantifier)
 SELECT COUNT(*) OVER w
 FROM rpr_reluctant
 WINDOW w AS (
@@ -2203,7 +2203,32 @@ SELECT pg_get_viewdef('rpr_serial_v8'::regclass);
 (1 row)
 
 DROP VIEW rpr_serial_v8;
-DROP TABLE rpr_serial;
+-- Reluctant {1}? quantifier deparse through ruleutils
+CREATE VIEW rpr_quant_reluctant_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{1}? B)
+             DEFINE A AS val > 0, B AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_reluctant_v'::regclass);
+                                pg_get_viewdef                                
+------------------------------------------------------------------------------
+  SELECT id,                                                                 +
+     val,                                                                    +
+     count(*) OVER w AS count                                                +
+    FROM rpr_serial                                                          +
+   WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
+   AFTER MATCH SKIP PAST LAST ROW                                            +
+   INITIAL                                                                   +
+   PATTERN (a{1}? b)                                                         +
+   DEFINE                                                                    +
+   a AS (val > 0),                                                           +
+   b AS (val > 0) );
+(1 row)
+
+DROP VIEW rpr_quant_reluctant_v;
 -- Materialized view (if supported)
 CREATE TABLE rpr_mview (id INT, val INT);
 INSERT INTO rpr_mview VALUES (1, 10), (2, 20), (3, 30);
@@ -2251,6 +2276,52 @@ SELECT * FROM rpr_mview_v1 ORDER BY id;
 
 DROP MATERIALIZED VIEW rpr_mview_v1;
 DROP TABLE rpr_mview;
+-- CREATE TABLE AS SELECT with RPR
+CREATE TABLE rpr_ctas (id INT, val INT);
+INSERT INTO rpr_ctas VALUES (1, 10), (2, 20), (3, 15), (4, 25);
+CREATE TABLE rpr_ctas_result AS
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_ctas
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE A AS TRUE, B AS val > PREV(val)
+);
+SELECT * FROM rpr_ctas_result ORDER BY id;
+ id | val | cnt 
+----+-----+-----
+  1 |  10 |   2
+  2 |  20 |   0
+  3 |  15 |   2
+  4 |  25 |   0
+(4 rows)
+
+-- INSERT INTO ... SELECT with RPR
+CREATE TABLE rpr_insert_target (id INT, val INT, cnt BIGINT);
+INSERT INTO rpr_insert_target
+SELECT id, val, count(*) OVER w
+FROM rpr_ctas
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE A AS TRUE, B AS val > PREV(val)
+);
+SELECT * FROM rpr_insert_target ORDER BY id;
+ id | val | cnt 
+----+-----+-----
+  1 |  10 |   2
+  2 |  20 |   0
+  3 |  15 |   2
+  4 |  25 |   0
+(4 rows)
+
+DROP TABLE rpr_ctas_result;
+DROP TABLE rpr_insert_target;
+DROP TABLE rpr_ctas;
 -- Prepared statements (tests outfuncs.c / readfuncs.c)
 CREATE TABLE rpr_prep (id INT, val INT);
 INSERT INTO rpr_prep VALUES (1, 10), (2, 20), (3, 30);
@@ -2608,6 +2679,57 @@ SELECT pg_get_viewdef('rpr_multiwin_v'::regclass);
 
 DROP VIEW rpr_multiwin_v;
 DROP TABLE rpr_multiwin;
+-- {n} quantifier display in view
+CREATE VIEW rpr_quant_n_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{3})
+             DEFINE A AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_n_v'::regclass);
+                                pg_get_viewdef                                
+------------------------------------------------------------------------------
+  SELECT id,                                                                 +
+     val,                                                                    +
+     count(*) OVER w AS count                                                +
+    FROM rpr_serial                                                          +
+   WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
+   AFTER MATCH SKIP PAST LAST ROW                                            +
+   INITIAL                                                                   +
+   PATTERN (a{3})                                                            +
+   DEFINE                                                                    +
+   a AS (val > 0) );
+(1 row)
+
+DROP VIEW rpr_quant_n_v;
+-- {n,} quantifier display in view
+CREATE VIEW rpr_quant_n_plus_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{2,})
+             DEFINE A AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_n_plus_v'::regclass);
+                                pg_get_viewdef                                
+------------------------------------------------------------------------------
+  SELECT id,                                                                 +
+     val,                                                                    +
+     count(*) OVER w AS count                                                +
+    FROM rpr_serial                                                          +
+   WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING +
+   AFTER MATCH SKIP PAST LAST ROW                                            +
+   INITIAL                                                                   +
+   PATTERN (a{2,})                                                           +
+   DEFINE                                                                    +
+   a AS (val > 0) );
+(1 row)
+
+DROP VIEW rpr_quant_n_plus_v;
+DROP TABLE rpr_serial;
 -- ============================================================
 -- Error Cases Tests
 -- ============================================================
@@ -2694,6 +2816,7 @@ WINDOW w AS (
 ERROR:  pattern variable qualified column reference "a.val" is not supported in DEFINE clause
 LINE 7:     DEFINE A AS A.val > 0
                         ^
+-- Expected: ERROR: pattern variable qualified column reference "a.val" is not supported
 -- PATTERN-only variable qualified name: not supported even without DEFINE entry
 SELECT COUNT(*) OVER w
 FROM rpr_err
@@ -2706,6 +2829,7 @@ WINDOW w AS (
 ERROR:  pattern variable qualified column reference "b.val" is not supported in DEFINE clause
 LINE 7:     DEFINE A AS B.val > 0
                         ^
+-- Expected: ERROR: pattern variable qualified column reference "b.val" is not supported
 -- DEFINE-only variable qualified name: still a pattern variable, not a range variable
 SELECT COUNT(*) OVER w
 FROM rpr_err
@@ -2718,6 +2842,7 @@ WINDOW w AS (
 ERROR:  pattern variable qualified column reference "b.val" is not supported in DEFINE clause
 LINE 7:     DEFINE A AS val > 0, B AS B.val > 0
                                       ^
+-- Expected: ERROR: pattern variable qualified column reference "b.val" is not supported
 -- FROM-clause range variable qualified name: not allowed (prohibited by §6.5)
 SELECT COUNT(*) OVER w
 FROM rpr_err
@@ -2730,6 +2855,7 @@ WINDOW w AS (
 ERROR:  range variable qualified column reference "rpr_err.val" is not allowed in DEFINE clause
 LINE 7:     DEFINE A AS rpr_err.val > 0
                         ^
+-- Expected: ERROR: range variable qualified column reference "rpr_err.val" is not allowed
 -- Semantic errors
 -- Undefined column in DEFINE
 SELECT COUNT(*) OVER w
@@ -2769,7 +2895,7 @@ WINDOW w AS (
 ERROR:  aggregate functions are not allowed in DEFINE
 LINE 7:     DEFINE A AS COUNT(*) > 0
                         ^
--- Expected: ERROR or works depending on implementation
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 -- Subquery in DEFINE (NOT SUPPORTED)
 SELECT COUNT(*) OVER w
 FROM rpr_err
@@ -3019,6 +3145,26 @@ WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
          ->  Seq Scan on rpr_plan
 (6 rows)
 
+-- Data execution: SEQ flatten produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP TO NEXT ROW
+             PATTERN (A ((B) (C))) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
+ id | val | cnt 
+----+-----+-----
+  1 |  10 |   0
+  2 |  20 |   0
+  3 |  30 |   0
+  4 |  40 |   0
+  5 |  50 |   0
+  6 |  60 |   0
+  7 |  70 |   0
+  8 |  80 |   0
+  9 |  90 |   0
+ 10 | 100 |   0
+(10 rows)
+
 -- ALT flatten: (A | (B | C))+ -> (a | b | c)+
 EXPLAIN (COSTS OFF)
 SELECT COUNT(*) OVER w FROM rpr_plan
@@ -3049,6 +3195,26 @@ WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
          ->  Seq Scan on rpr_plan
 (6 rows)
 
+-- Data execution: ALT dedup produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP PAST LAST ROW
+             PATTERN ((A | B | A)+) DEFINE A AS val <= 50, B AS val > 50);
+ id | val | cnt 
+----+-----+-----
+  1 |  10 |  10
+  2 |  20 |   0
+  3 |  30 |   0
+  4 |  40 |   0
+  5 |  50 |   0
+  6 |  60 |   0
+  7 |  70 |   0
+  8 |  80 |   0
+  9 |  90 |   0
+ 10 | 100 |   0
+(10 rows)
+
 -- Quantifier multiply: (A{2}){3} -> a{6}
 EXPLAIN (COSTS OFF)
 SELECT COUNT(*) OVER w FROM rpr_plan
@@ -3468,6 +3634,26 @@ WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
          ->  Seq Scan on rpr_plan
 (6 rows)
 
+-- Data execution: GROUP unwrap produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP TO NEXT ROW
+             PATTERN ((A | B | C)) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
+ id | val | cnt 
+----+-----+-----
+  1 |  10 |   1
+  2 |  20 |   1
+  3 |  30 |   1
+  4 |  40 |   1
+  5 |  50 |   1
+  6 |  60 |   1
+  7 |  70 |   1
+  8 |  80 |   1
+  9 |  90 |   1
+ 10 | 100 |   1
+(10 rows)
+
 -- Reluctant optimization bypass: VAR merge
 -- A+? A stays as a+? a (greedy A+ A merges to a{2,})
 EXPLAIN (COSTS OFF)
@@ -3612,6 +3798,134 @@ WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
          ->  Seq Scan on rpr_plan
 (6 rows)
 
+-- Duplicate GROUP removal: ((A | B)+ | (A | B)+) -> (a | b)+
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN ((A | B)+ | (A | B)+) DEFINE A AS val <= 50, B AS val > 50);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: (a | b)+
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- Consecutive VAR merge with zero-min: A* A+ -> a+
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A* A+) DEFINE A AS val > 0);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: a+"
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- Consecutive VAR merge (4-element): A A{2} A+ A{3} -> a{7,}
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A A{2} A+ A{3}) DEFINE A AS val > 0);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: a{7,}"
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- PREFIX+SUFFIX merge (5-way): A B A B (A B)+ A B A B -> (a b){5,}
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A B A B (A B)+ A B A B)
+             DEFINE A AS val <= 50, B AS val > 50);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: (a' b'){5,}"
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- Unwrap single-item ALT after dedup: (A | A)+ -> a+
+-- ALT dedup reduces to single-item, then GROUP unwrap
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN ((A | A)+) DEFINE A AS val > 0);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: a+"
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- GROUP{1,1} to SEQ with flatten: ((A B)(C D)) -> a b c d
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A B)(C D)))
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: a b c d
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- Nested ALT pattern: ((A B) | C) D | A B C
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A B) | C) D | A B C)
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: ((a b | c) d | a b c)
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
+-- Nested ALT with unbounded: ((A+ B) | C) D | A B C
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A+ B) | C) D | A B C)
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+                                  QUERY PLAN                                   
+-------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: ((a+" b | c) d | a b c)
+   ->  Sort
+         Sort Key: id
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
 -- ============================================================
 -- Absorption Flag Display Tests
 -- ============================================================
@@ -3801,6 +4115,28 @@ WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND 10 FOLLOWING
          ->  Seq Scan on rpr_plan
 (6 rows)
 
+-- Reluctant {1}? quantifier deparse
+-- A{1}? is a reluctant {1,1} quantifier.  The deparse code must
+-- output "{1}" explicitly to disambiguate from a bare "?" quantifier
+-- (which would mean {0,1}).
+EXPLAIN (COSTS OFF) SELECT count(*) OVER w
+FROM rpr_plan
+WINDOW w AS (
+    ORDER BY val
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    PATTERN (A{1}? B)
+    DEFINE A AS val > 0, B AS val > 0
+);
+                                   QUERY PLAN                                   
+--------------------------------------------------------------------------------
+ WindowAgg
+   Window: w AS (ORDER BY val ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+   Pattern: a{1}? b
+   ->  Sort
+         Sort Key: val
+         ->  Seq Scan on rpr_plan
+(6 rows)
+
 -- ============================================================
 -- Absorption Analysis Tests
 -- ============================================================
@@ -4529,7 +4865,8 @@ ORDER BY category;
 ERROR:  syntax error at or near "GROUP"
 LINE 12: GROUP BY category
          ^
--- Expected: ERROR (GROUP BY with window RPR not supported)
+-- Expected: ERROR: syntax error at or near "GROUP"
+-- (GROUP BY after WINDOW clause is not valid SQL syntax)
 -- ============================================================
 -- Subquery and CTE Tests
 -- Files: planner.c, prepjointree.c
@@ -5007,7 +5344,8 @@ CREATE TABLE rpr_sort (id INT, category VARCHAR(10), val INT);
 INSERT INTO rpr_sort VALUES
     (1, 'A', 30), (2, 'B', 20), (3, 'A', 10),
     (4, 'B', 40), (5, 'A', 50), (6, 'B', 60);
--- RPR with GROUP BY
+-- RPR with GROUP BY (aggregate in DEFINE → ERROR before GROUP BY interaction)
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 SELECT category,
        COUNT(*) as group_cnt,
        MAX(val) as max_val,
@@ -5024,7 +5362,8 @@ ORDER BY category;
 ERROR:  aggregate functions are not allowed in DEFINE
 LINE 11:     DEFINE A AS COUNT(*) > 0
                          ^
--- RPR with HAVING
+-- RPR with HAVING (same aggregate-in-DEFINE error)
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 SELECT category,
        COUNT(*) as group_cnt,
        COUNT(*) OVER w as window_cnt
@@ -5787,6 +6126,3 @@ FROM (SELECT id, val,
 (2 rows)
 
 DROP TABLE rpr_plan;
--- ============================================================
--- End of rpr_base.sql
--- ============================================================
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index 340408cc454..817269021f4 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -6,8 +6,9 @@
 -- This test suite validates EXPLAIN output for RPR queries,
 -- including NFA statistics shown in EXPLAIN ANALYZE:
 --   - NFA States: peak, total, merged
---   - NFA Contexts: peak, total, absorbed, skipped
+--   - NFA Contexts: peak, total, pruned
 --   - NFA: matched (len min/max/avg), mismatched (len min/max/avg)
+--   - NFA: absorbed (len min/max/avg), skipped (len min/max/avg)
 --   - Pattern deparse formatting
 --   - Multiple output formats (text, JSON, XML)
 --
@@ -434,7 +435,7 @@ WINDOW w AS (
 (9 rows)
 
 DROP VIEW rpr_v;
--- Grouped pattern with quantifier - state merging
+-- Grouped pattern with quantifier - state count with grouping
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
@@ -637,7 +638,7 @@ WINDOW w AS (
 (9 rows)
 
 DROP VIEW rpr_v;
--- High state merging - alternation with plus quantifier
+-- High state count - alternation with plus quantifier
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
@@ -758,7 +759,7 @@ WINDOW w AS (
 
 DROP VIEW rpr_v;
 -- ============================================================
--- Context Statistics Tests (peak, total, absorbed, skipped)
+-- Context Statistics Tests (peak, total, pruned + absorbed/skipped)
 -- ============================================================
 -- Context absorption with unbounded quantifier at start
 CREATE TEMP VIEW rpr_v AS
@@ -1047,7 +1048,7 @@ WINDOW w AS (
 (9 rows)
 
 DROP VIEW rpr_v;
--- Mix of short and long matches
+-- Uniform match length with mismatches from gap rows (v%20 = 11..15)
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
@@ -1094,8 +1095,8 @@ DROP VIEW rpr_v;
 -- ============================================================
 -- Mismatch Length Statistics Tests
 -- ============================================================
--- Pattern that causes mismatches with length > 1
--- Mismatch happens when partial match fails after processing multiple rows
+-- Pattern with complete match every cycle: 0 mismatched
+-- A(1,2,3) B(4,5) C(6) repeats perfectly; X rows are pruned, not mismatched
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM (
@@ -1374,87 +1375,6 @@ WINDOW w AS (
  ]
 (1 row)
 
-DROP VIEW rpr_v;
--- ============================================================
--- XML Format Tests
--- ============================================================
--- XML format output
-CREATE TEMP VIEW rpr_v AS
-SELECT count(*) OVER w
-FROM generate_series(1, 30) AS s(v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B)
-    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
-);
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
-       line       
-------------------
-   PATTERN (a b) 
-(1 row)
-
-SELECT rpr_explain_filter('
-EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
-SELECT count(*) OVER w
-FROM generate_series(1, 30) AS s(v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B)
-    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
-)');
-                               rpr_explain_filter                               
---------------------------------------------------------------------------------
- <explain xmlns="http://www.postgresql.org/2009/explain";                      +
-   <Query>                                                                     +
-     <Plan>                                                                    +
-       <Node-Type>WindowAgg</Node-Type>                                        +
-       <Parallel-Aware>false</Parallel-Aware>                                  +
-       <Async-Capable>false</Async-Capable>                                    +
-       <Actual-Rows>30.00</Actual-Rows>                                        +
-       <Actual-Loops>1</Actual-Loops>                                          +
-       <Disabled>false</Disabled>                                              +
-       <Window>w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)</Window>+
-       <Pattern>a b</Pattern>                                                  +
-       <Storage>Memory</Storage>                                               +
-       <Maximum-Storage>0</Maximum-Storage>                                    +
-       <NFA-States-Peak>2</NFA-States-Peak>                                    +
-       <NFA-States-Total>31</NFA-States-Total>                                 +
-       <NFA-States-Merged>0</NFA-States-Merged>                                +
-       <NFA-Contexts-Peak>2</NFA-Contexts-Peak>                                +
-       <NFA-Contexts-Total>31</NFA-Contexts-Total>                             +
-       <NFA-Contexts-Absorbed>0</NFA-Contexts-Absorbed>                        +
-       <NFA-Contexts-Skipped>15</NFA-Contexts-Skipped>                         +
-       <NFA-Contexts-Pruned>0</NFA-Contexts-Pruned>                            +
-       <NFA-Matched>15</NFA-Matched>                                           +
-       <NFA-Mismatched>0</NFA-Mismatched>                                      +
-       <NFA-Match-Length-Min>2</NFA-Match-Length-Min>                          +
-       <NFA-Match-Length-Max>2</NFA-Match-Length-Max>                          +
-       <NFA-Match-Length-Avg>2.0</NFA-Match-Length-Avg>                        +
-       <NFA-Skipped-Length-Min>1</NFA-Skipped-Length-Min>                      +
-       <NFA-Skipped-Length-Max>1</NFA-Skipped-Length-Max>                      +
-       <NFA-Skipped-Length-Avg>1.0</NFA-Skipped-Length-Avg>                    +
-       <Plans>                                                                 +
-         <Plan>                                                                +
-           <Node-Type>Function Scan</Node-Type>                                +
-           <Parent-Relationship>Outer</Parent-Relationship>                    +
-           <Parallel-Aware>false</Parallel-Aware>                              +
-           <Async-Capable>false</Async-Capable>                                +
-           <Function-Name>generate_series</Function-Name>                      +
-           <Alias>s</Alias>                                                    +
-           <Actual-Rows>30.00</Actual-Rows>                                    +
-           <Actual-Loops>1</Actual-Loops>                                      +
-           <Disabled>false</Disabled>                                          +
-         </Plan>                                                               +
-       </Plans>                                                                +
-     </Plan>                                                                   +
-     <Triggers>                                                                +
-     </Triggers>                                                               +
-   </Query>                                                                    +
- </explain>
-(1 row)
-
 DROP VIEW rpr_v;
 -- JSON format with mismatch statistics
 -- Pattern A B C expects 1,2,3 but gets 1,2,4 twice causing mismatches
@@ -1618,6 +1538,87 @@ WINDOW w AS (
  ]
 (1 row)
 
+DROP VIEW rpr_v;
+-- ============================================================
+-- XML Format Tests
+-- ============================================================
+-- XML format output
+CREATE TEMP VIEW rpr_v AS
+SELECT count(*) OVER w
+FROM generate_series(1, 30) AS s(v)
+WINDOW w AS (
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B)
+    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
+);
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+       line       
+------------------
+   PATTERN (a b) 
+(1 row)
+
+SELECT rpr_explain_filter('
+EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
+SELECT count(*) OVER w
+FROM generate_series(1, 30) AS s(v)
+WINDOW w AS (
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B)
+    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
+)');
+                               rpr_explain_filter                               
+--------------------------------------------------------------------------------
+ <explain xmlns="http://www.postgresql.org/2009/explain";                      +
+   <Query>                                                                     +
+     <Plan>                                                                    +
+       <Node-Type>WindowAgg</Node-Type>                                        +
+       <Parallel-Aware>false</Parallel-Aware>                                  +
+       <Async-Capable>false</Async-Capable>                                    +
+       <Actual-Rows>30.00</Actual-Rows>                                        +
+       <Actual-Loops>1</Actual-Loops>                                          +
+       <Disabled>false</Disabled>                                              +
+       <Window>w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)</Window>+
+       <Pattern>a b</Pattern>                                                  +
+       <Storage>Memory</Storage>                                               +
+       <Maximum-Storage>0</Maximum-Storage>                                    +
+       <NFA-States-Peak>2</NFA-States-Peak>                                    +
+       <NFA-States-Total>31</NFA-States-Total>                                 +
+       <NFA-States-Merged>0</NFA-States-Merged>                                +
+       <NFA-Contexts-Peak>2</NFA-Contexts-Peak>                                +
+       <NFA-Contexts-Total>31</NFA-Contexts-Total>                             +
+       <NFA-Contexts-Absorbed>0</NFA-Contexts-Absorbed>                        +
+       <NFA-Contexts-Skipped>15</NFA-Contexts-Skipped>                         +
+       <NFA-Contexts-Pruned>0</NFA-Contexts-Pruned>                            +
+       <NFA-Matched>15</NFA-Matched>                                           +
+       <NFA-Mismatched>0</NFA-Mismatched>                                      +
+       <NFA-Match-Length-Min>2</NFA-Match-Length-Min>                          +
+       <NFA-Match-Length-Max>2</NFA-Match-Length-Max>                          +
+       <NFA-Match-Length-Avg>2.0</NFA-Match-Length-Avg>                        +
+       <NFA-Skipped-Length-Min>1</NFA-Skipped-Length-Min>                      +
+       <NFA-Skipped-Length-Max>1</NFA-Skipped-Length-Max>                      +
+       <NFA-Skipped-Length-Avg>1.0</NFA-Skipped-Length-Avg>                    +
+       <Plans>                                                                 +
+         <Plan>                                                                +
+           <Node-Type>Function Scan</Node-Type>                                +
+           <Parent-Relationship>Outer</Parent-Relationship>                    +
+           <Parallel-Aware>false</Parallel-Aware>                              +
+           <Async-Capable>false</Async-Capable>                                +
+           <Function-Name>generate_series</Function-Name>                      +
+           <Alias>s</Alias>                                                    +
+           <Actual-Rows>30.00</Actual-Rows>                                    +
+           <Actual-Loops>1</Actual-Loops>                                      +
+           <Disabled>false</Disabled>                                          +
+         </Plan>                                                               +
+       </Plans>                                                                +
+     </Plan>                                                                   +
+     <Triggers>                                                                +
+     </Triggers>                                                               +
+   </Query>                                                                    +
+ </explain>
+(1 row)
+
 DROP VIEW rpr_v;
 -- ============================================================
 -- Multiple Partitions Tests
diff --git a/src/test/regress/expected/rpr_nfa.out b/src/test/regress/expected/rpr_nfa.out
index 03ee174d359..c8464e8fcf4 100644
--- a/src/test/regress/expected/rpr_nfa.out
+++ b/src/test/regress/expected/rpr_nfa.out
@@ -322,6 +322,161 @@ WINDOW w AS (
   5 | {_}   |             |          
 (5 rows)
 
+-- Absorption with fixed suffix: A+ B
+WITH test_absorb_suffix AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_suffix
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {A}   |           2 |         4
+  3 | {A}   |           3 |         4
+  4 | {B}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
+-- Per-branch absorption with ALT: B+ C | B+ D
+WITH test_absorb_alt AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['B']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B']),
+        (4, ARRAY['D']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_alt
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (B+ C | B+ D)
+    DEFINE
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {B}   |           1 |         4
+  2 | {B}   |           2 |         4
+  3 | {B}   |           3 |         4
+  4 | {D}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
+-- Non-absorbable: A B+ (unbounded not in first position)
+WITH test_no_absorb AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_no_absorb
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {B}   |             |          
+  3 | {B}   |             |          
+  4 | {B}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
+-- GROUP merge enables absorption: (A B) (A B)+ optimized to (A B){2,}
+WITH test_absorb_group AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B']),
+        (7, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_group
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN ((A B) (A B)+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         6
+  2 | {B}   |             |          
+  3 | {A}   |           3 |         6
+  4 | {B}   |             |          
+  5 | {A}   |             |          
+  6 | {B}   |             |          
+  7 | {X}   |             |          
+(7 rows)
+
+-- Multiple unbounded: A+ B+ (first element unbounded enables absorption)
+WITH test_multi_unbounded AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['B']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_multi_unbounded
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {A}   |           2 |         4
+  3 | {B}   |             |          
+  4 | {B}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
 -- ============================================================
 -- Context Lifecycle
 -- ============================================================
@@ -674,7 +829,8 @@ WINDOW w AS (
 (5 rows)
 
 -- Optional reluctant group: (A B)?? C
--- nfa_advance_begin: reluctant prefers skip (0 iterations) over enter
+-- nfa_advance_begin: reluctant tries skip first, but skip path needs C
+-- at row 1 which is A → skip fails. Enter path succeeds: A(1) B(2) C(3).
 WITH test_optional_reluctant AS (
     SELECT * FROM (VALUES
         (1, ARRAY['A']),
@@ -734,6 +890,44 @@ WINDOW w AS (
   4 | {B}   |             |          
 (4 rows)
 
+-- Reluctant optional group skip-to-FIN
+-- When a reluctant optional group's skip path reaches FIN, the group
+-- entry path is abandoned (nodeWindowAgg.c nfa_advance_begin).
+-- Pattern: C (A B)?? -- after C matches, the reluctant group (A B)??
+-- prefers to skip.  Skip goes to FIN (group is last element), so
+-- the match completes with just C.
+WITH test_begin_skip_fin AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['C']),
+        (2, ARRAY['A']),
+        (3, ARRAY['B']),
+        (4, ARRAY['C','A']),
+        (5, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_begin_skip_fin
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (C (A B)??)
+    DEFINE
+        C AS 'C' = ANY(flags),
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {C}   |           1 |         1
+  2 | {A}   |             |          
+  3 | {B}   |             |          
+  4 | {C,A} |           4 |         4
+  5 | {B}   |             |          
+(5 rows)
+
 -- ============================================================
 -- Match Phase
 -- ============================================================
@@ -1664,6 +1858,64 @@ WINDOW w AS (
   4 | {B}   |             |          
 (4 rows)
 
+-- A+? B (reluctant plus): exits A at first B availability
+-- (Same scenario as greedy-vs-reluctant comparison above; retained for
+-- standalone quantifier coverage alongside A{1,3}? and A{2,3}? below)
+WITH test_reluctant_plus AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['A','_']),
+        (3, ARRAY['A','B']),
+        (4, ARRAY['B','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_reluctant_plus
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A+? B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A,_} |           1 |         3
+  2 | {A,_} |             |          
+  3 | {A,B} |             |          
+  4 | {B,_} |             |          
+(4 rows)
+
+-- A{1,3}? B (reluctant bounded): same data, bounded quantifier
+WITH test_reluctant_bounded AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['A','_']),
+        (3, ARRAY['A','B']),
+        (4, ARRAY['B','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_reluctant_bounded
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A{1,3}? B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A,_} |           1 |         3
+  2 | {A,_} |             |          
+  3 | {A,B} |             |          
+  4 | {B,_} |             |          
+(4 rows)
+
 -- ============================================================
 -- Pathological Pattern Runtime Protection
 -- ============================================================
@@ -2030,6 +2282,283 @@ WINDOW w AS (
   3 | {B}   |           3 |         3
 (3 rows)
 
+-- Overlapping match: A B C D E | B C D | C D E F (SKIP PAST LAST ROW)
+WITH test_overlap1 AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['E']),
+        (6, ARRAY['F'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C D E | B C D | C D E F)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags),
+        F AS 'F' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         5
+  2 | {B}   |             |          
+  3 | {C}   |             |          
+  4 | {D}   |             |          
+  5 | {E}   |             |          
+  6 | {F}   |             |          
+(6 rows)
+
+-- Same with SKIP TO NEXT ROW: three overlapping matches
+WITH test_overlap1 AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['E']),
+        (6, ARRAY['F'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B C D E | B C D | C D E F)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags),
+        F AS 'F' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         5
+  2 | {B}   |           2 |         4
+  3 | {C}   |           3 |         6
+  4 | {D}   |             |          
+  5 | {E}   |             |          
+  6 | {F}   |             |          
+(6 rows)
+
+-- Longer pattern fails, shorter survives: A+ B C D E | B+ C
+WITH test_overlap1b AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1b
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A+ B C D E | B+ C)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |             |          
+  2 | {B}   |           2 |         3
+  3 | {C}   |             |          
+  4 | {D}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
+-- Long B sequence with different endings: A B+ C | B+ D
+WITH test_overlap2 AS (
+    SELECT * FROM (VALUES
+        (1,  ARRAY['A']),
+        (2,  ARRAY['B']),
+        (3,  ARRAY['B']),
+        (4,  ARRAY['B']),
+        (5,  ARRAY['B']),
+        (6,  ARRAY['C']),
+        (7,  ARRAY['B']),
+        (8,  ARRAY['B']),
+        (9,  ARRAY['B']),
+        (10, ARRAY['D'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap2
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B+ C | B+ D)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         6
+  2 | {B}   |             |          
+  3 | {B}   |             |          
+  4 | {B}   |             |          
+  5 | {B}   |             |          
+  6 | {C}   |             |          
+  7 | {B}   |           7 |        10
+  8 | {B}   |           8 |        10
+  9 | {B}   |           9 |        10
+ 10 | {D}   |             |          
+(10 rows)
+
+-- Greedy with late failure ("betrayal"): A B C+ D | A B
+WITH test_betrayal AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['C']),
+        (5, ARRAY['C']),
+        (6, ARRAY['E'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_betrayal
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C+ D | A B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         2
+  2 | {B}   |             |          
+  3 | {C}   |             |          
+  4 | {C}   |             |          
+  5 | {C}   |             |          
+  6 | {E}   |             |          
+(6 rows)
+
+-- Multiple TRUE per row: overlapping pattern variables
+WITH test_multi_true AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','B']),
+        (2, ARRAY['B','C']),
+        (3, ARRAY['C','D']),
+        (4, ARRAY['D','E']),
+        (5, ARRAY['E','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_multi_true
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C D E)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A,B} |           1 |         5
+  2 | {B,C} |             |          
+  3 | {C,D} |             |          
+  4 | {D,E} |             |          
+  5 | {E,_} |             |          
+(5 rows)
+
+-- Diagonal pattern with shifted multi-TRUE overlap
+WITH test_diagonal AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['B','A']),
+        (3, ARRAY['C','B']),
+        (4, ARRAY['D','C']),
+        (5, ARRAY['_','D'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_diagonal
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B C D)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A,_} |           1 |         4
+  2 | {B,A} |           2 |         5
+  3 | {C,B} |             |          
+  4 | {D,C} |             |          
+  5 | {_,D} |             |          
+(5 rows)
+
+-- ((A | B) C)+ - alternation inside group with outer quantifier
+WITH test_alt_group AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['C']),
+        (3, ARRAY['B']),
+        (4, ARRAY['C']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_alt_group
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (((A | B) C)+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {C}   |             |          
+  3 | {B}   |             |          
+  4 | {C}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
 -- ============================================================
 -- Deep Nested Groups
 -- ============================================================
@@ -2227,6 +2756,69 @@ WINDOW w AS (
   5 | {C}   |             |          
 (5 rows)
 
+-- (A B){2} - group with exact quantifier
+WITH test_group_exact AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_group_exact
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN ((A B){2})
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {B}   |             |          
+  3 | {A}   |             |          
+  4 | {B}   |             |          
+  5 | {X}   |             |          
+(5 rows)
+
+-- Nested END->END fast-forward
+-- When an inner group has a nullable body and count < min, the
+-- fast-forward path exits through the outer END, incrementing
+-- the outer group's count (nodeWindowAgg.c nfa_advance_end).
+-- Pattern: ((A?){2,3}){2,3} -- nested groups, neither collapses
+-- because the optimizer cannot safely multiply non-exact quantifiers.
+-- Data has no A rows, forcing all-empty iterations via fast-forward.
+WITH test_nested_ff AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['B']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_nested_ff
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (((A?){2,3}){2,3})
+    DEFINE
+        A AS 'A' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {B}   |             |          
+  2 | {B}   |             |          
+  3 | {B}   |             |          
+(3 rows)
+
 -- ============================================================
 -- SKIP Options (Runtime)
 -- ============================================================
@@ -2390,6 +2982,8 @@ ORDER BY mode, id;
 
 -- ============================================================
 -- INITIAL Mode (Runtime)
+-- Placeholder: INITIAL is not yet implemented (syntax error).
+-- Kept here so tests convert to runtime tests when implemented.
 -- ============================================================
 -- INITIAL mode (not yet supported - produces syntax error)
 WITH test_initial_mode AS (
@@ -2597,6 +3191,80 @@ WINDOW w AS (
   5 | {B}   |             |          
 (5 rows)
 
+-- N FOLLOWING + SKIP TO NEXT ROW: overlapping matches bounded by frame
+-- Row 1: frame [1,4], A(1-3) B(4) -> match
+-- Row 2: frame [2,5], A(2-3) B(4) -> match
+-- Row 3: frame [3,6], A(3) B(4) -> match
+-- Row 5: frame [5,6], A(5) B(6) -> match
+WITH test_n_skip_next AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_n_skip_next
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |           1 |         4
+  2 | {A}   |           2 |         4
+  3 | {A}   |           3 |         4
+  4 | {B}   |             |          
+  5 | {A}   |           5 |         6
+  6 | {B}   |             |          
+(6 rows)
+
+-- Frame exactly 1 row short of potential match
+-- From row 1: A A A B needs 4 rows but frame holds 3 -> no match
+-- From row 2: A A B fits in 3-row frame -> match
+WITH test_frame_one_short AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_frame_one_short
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+ id | flags | match_start | match_end 
+----+-------+-------------+-----------
+  1 | {A}   |             |          
+  2 | {A}   |           2 |         4
+  3 | {A}   |           3 |         4
+  4 | {B}   |             |          
+  5 | {A}   |           5 |         6
+  6 | {B}   |             |          
+(6 rows)
+
 -- ============================================================
 -- Special Partition Cases
 -- ============================================================
@@ -3407,7 +4075,8 @@ WINDOW w AS (
 
 -- (A?){0,3}: min=0, nullable inner.
 -- A never matches. A? matches empty, min=0 satisfied immediately.
--- Expected: empty match (match_start = match_end) for every row
+-- Per standard: empty match expected for every row.
+-- XXX: visited bitmap blocks empty iteration → no match (same as {2,3})
 WITH test_728_min0 AS (
     SELECT * FROM (VALUES
         (1, ARRAY['B']),
@@ -3436,7 +4105,8 @@ WINDOW w AS (
 
 -- (A?){1,3}: min=1, nullable inner.
 -- A never matches. Need 1 empty iteration to satisfy min=1.
--- Expected: empty match for every row
+-- Per standard: empty match expected for every row.
+-- XXX: visited bitmap blocks empty iteration → no match (same as {2,3})
 WITH test_728_min1 AS (
     SELECT * FROM (VALUES
         (1, ARRAY['B']),
@@ -3497,7 +4167,8 @@ WINDOW w AS (
 -- (A?){2,3} mixed: some rows match A, some don't
 -- Rows 1-2: A matches, greedy takes 2 → min satisfied
 -- Row 3: A doesn't match, needs 2 empty iterations for min=2
--- Expected: all rows produce matches
+-- XXX: Row 3 fails due to visited bitmap (same as pure empty {2,3})
+-- Row 4: A matches 1 real iter + 1 ff empty exit → match 4-4
 WITH test_728_min2_mixed AS (
     SELECT * FROM (VALUES
         (1, ARRAY['A']),
diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql
index a263074e3e9..8dbd3f9036a 100644
--- a/src/test/regress/sql/rpr.sql
+++ b/src/test/regress/sql/rpr.sql
@@ -1,5 +1,10 @@
 --
--- Test for row pattern definition clause
+-- Test for row pattern recognition: WINDOW clause integration and
+-- real-world scenario tests using stock price data.
+--
+-- Parser/planner tests: rpr_base.sql
+-- NFA engine tests: rpr_nfa.sql
+-- EXPLAIN statistics tests: rpr_explain.sql
 --
 
 CREATE TEMP TABLE stock (
@@ -30,6 +35,10 @@ INSERT INTO stock VALUES ('company2', '2023-07-10', 1300);
 
 SELECT * FROM stock;
 
+--
+-- Basic pattern matching with PREV/NEXT
+--
+
 -- basic test using PREV
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w,
  nth_value(tdate, 2) OVER w AS nth_second
@@ -175,7 +184,7 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
   HIGH AS price > 150
 );
 
--- basic test with none-greedy pattern
+-- basic test with fixed-length pattern (A A A = exactly 3)
 SELECT company, tdate, price, count(*) OVER w
  FROM stock
  WINDOW w AS (
@@ -294,7 +303,9 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
   UPDOWN AS price > PREV(price) AND price > NEXT(price)
 );
 
--- using AFTER MATCH SKIP TO NEXT ROW
+-- using AFTER MATCH SKIP TO NEXT ROW (same pattern as above;
+-- match length is always 2, so result is identical to SKIP PAST LAST ROW.
+-- SKIP TO NEXT ROW's distinct effect is tested in backtracking section.)
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
  FROM stock
  WINDOW w AS (
@@ -308,8 +319,92 @@ SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER
   UPDOWN AS price > PREV(price) AND price > NEXT(price)
 );
 
--- match everything
+-- PREV returns NULL at partition's first row (null_slot path)
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ PATTERN (BOUNDARY REST+)
+ DEFINE
+  BOUNDARY AS PREV(price) IS NULL,
+  REST AS PREV(price) IS NOT NULL
+);
+
+-- NEXT returns NULL at partition's last row (null_slot path)
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+ BOUNDARY)
+ DEFINE
+  A AS NEXT(price) IS NOT NULL,
+  BOUNDARY AS NEXT(price) IS NULL
+);
+
+-- DESC order: PREV refers to the row with later date
+SELECT company, tdate, price, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate DESC
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (START DOWN+ UP+)
+ DEFINE
+  START AS TRUE,
+  DOWN AS price < PREV(price),
+  UP AS price > PREV(price)
+);
+
+-- Multiple partitions with unequal sizes
+WITH multi_part AS (
+ SELECT * FROM (VALUES
+  ('a', 1, 10), ('a', 2, 20), ('a', 3, 15),
+  ('b', 1, 5),
+  ('c', 1, 100), ('c', 2, 200), ('c', 3, 150), ('c', 4, 140), ('c', 5, 300)
+ ) AS t(grp, id, val)
+)
+SELECT grp, id, val, count(*) OVER w
+FROM multi_part
+WINDOW w AS (
+ PARTITION BY grp
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+)
+ DEFINE
+  A AS val <= NEXT(val),
+  B AS val > PREV(val) OR val < PREV(val)
+);
+
+-- FLOAT/NUMERIC DEFINE conditions
+WITH float_data AS (
+ SELECT * FROM (VALUES
+  (1, 1.0::float8), (2, 1.5), (3, 1.4999), (4, 1.50001), (5, 0.1)
+ ) AS t(id, val)
+)
+SELECT id, val, count(*) OVER w
+FROM float_data
+WINDOW w AS (
+ ORDER BY id
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+)
+ DEFINE
+  A AS TRUE,
+  B AS val > PREV(val) * 0.99
+);
+
+--
+-- SKIP TO / Backtracking / Frame boundary
+--
 
+-- match everything
 SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
  FROM stock
  WINDOW w AS (
@@ -474,6 +569,25 @@ UP AS price > PREV(price),
 DOWN AS price < PREV(price)
 );
 
+-- row_number() within RPR reduced frame
+SELECT company, tdate, price, row_number() OVER w, count(*) OVER w
+FROM stock
+WINDOW w AS (
+ PARTITION BY company
+ ORDER BY tdate
+ ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (START UP+ DOWN+)
+ DEFINE
+  START AS TRUE,
+  UP AS price > PREV(price),
+  DOWN AS price < PREV(price)
+);
+
+--
+-- SQL Integration: JOIN, CTE, LATERAL
+--
+
 -- JOIN case
 CREATE TEMP TABLE t1 (i int, v1 int);
 CREATE TEMP TABLE t2 (j int, v2 int);
@@ -552,6 +666,10 @@ SELECT id, i, j, count(*) OVER w
   COND AS PREV(i + j + 1) < 10
 );
 
+--
+-- Large-scale / scalability tests
+--
+
 -- Smoke test for larger partitions.
 WITH s AS (
  SELECT v, count(*) OVER w AS c
@@ -607,8 +725,9 @@ result AS (
 SELECT match_first, match_last, match_len FROM result WHERE match_len > 0;
 
 --
--- Using IGNORE NULLS
+-- IGNORE NULLS
 --
+
 -- no NULL rows case. The result should be identical with "basic test using PREV"
 SELECT company, tdate, price, first_value(price) IGNORE NULLS OVER w,
  last_value(price) IGNORE NULLS OVER w,
@@ -626,7 +745,7 @@ SELECT company, tdate, price, first_value(price) IGNORE NULLS OVER w,
 );
 
 -- nth_value with IGNORE NULLS option wants to find the second row but
--- due a NULL in the midlle, it returns the third row.
+-- due to a NULL in the middle, it returns the third row.
 WITH data AS (
  SELECT * FROM (VALUES
   (10, 1), (11, NULL), (12, 3), (13, 4)
@@ -643,8 +762,8 @@ WITH data AS (
   );
 
 -- nth_value with IGNORE NULLS option wants to find the third row but
--- due a NULL in the midlle, it reaches the end of reduced frame and
--- return NULL
+-- due to a NULL in the middle, it reaches the end of reduced frame and
+-- returns NULL
 WITH data AS (
  SELECT * FROM (VALUES
   (10, 1), (11, NULL), (12, 3), (13, 4)
@@ -677,1503 +796,115 @@ WINDOW w AS (
   DOWN AS price < PREV(price)
 );
 
--- View and pg_get_viewdef tests.
-CREATE TEMP VIEW v_window AS
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w,
- nth_value(tdate, 2) OVER w AS nth_second
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-
-SELECT * FROM v_window;
-SELECT pg_get_viewdef('v_window');
-
---
--- Pattern optimization tests
--- VIEW shows original pattern, EXPLAIN shows optimized pattern
---
-
--- Test: duplicate alternatives removal (A | B | A)+ -> (A | B)+
-CREATE TEMP VIEW v_opt_dup AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | B | A)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_dup');  -- original: ((a | b | a)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_dup;  -- optimized: ((a | b)+)
-
--- Test: duplicate group removal ((A | B)+ | (A | B)+) -> (A | B)+
-CREATE TEMP VIEW v_opt_dup_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | B)+ | (A | B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_dup_group');  -- original: ((a | b)+ | (a | b)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_dup_group;  -- optimized: ((a | b)+)
-
--- Test: consecutive vars merge (A A A) -> A{3}
-CREATE TEMP VIEW v_opt_merge AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A A)
- DEFINE
-  A AS price >= 140 AND price <= 150
-);
-SELECT pg_get_viewdef('v_opt_merge');  -- original: (a a a)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge;  -- optimized: a{3}
-
--- Test: quantified vars merge (A A+ A) -> A{3,}
-CREATE TEMP VIEW v_opt_merge_quant AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A+ A)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_quant');  -- original: (a a+ a)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_quant;  -- optimized: a{3,}
-
--- Test: merge two unbounded (A+ A+) -> A{2,}
-CREATE TEMP VIEW v_opt_merge_unbounded AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- IGNORE NULLS + first_value where first value in reduced frame is NULL
+WITH data AS (
+ SELECT * FROM (VALUES
+  (1, NULL), (2, NULL), (3, 30), (4, 40)
+ ) AS t(id, val))
+SELECT id, val,
+ first_value(val) IGNORE NULLS OVER w AS fv_ignull,
+ count(*) OVER w
+FROM data
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A+ A+)
- DEFINE
-  A AS price > 100
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+)
+ DEFINE A AS TRUE
 );
-SELECT pg_get_viewdef('v_opt_merge_unbounded');  -- original: (a+ a+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_unbounded;  -- optimized: a{2,}
 
--- Test: merge with zero-min (A* A+) -> A+
-CREATE TEMP VIEW v_opt_merge_star AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- IGNORE NULLS + all values NULL in reduced frame
+WITH data AS (
+ SELECT * FROM (VALUES
+  (1, NULL), (2, NULL), (3, NULL)
+ ) AS t(id, val))
+SELECT id, val,
+ first_value(val) IGNORE NULLS OVER w AS fv_ignull,
+ last_value(val) IGNORE NULLS OVER w AS lv_ignull,
+ count(*) OVER w
+FROM data
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A* A+)
- DEFINE
-  A AS price > 100
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A+)
+ DEFINE A AS TRUE
 );
-SELECT pg_get_viewdef('v_opt_merge_star');  -- original: (a* a+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_star;  -- optimized: a+
 
--- Test: complex merge (A A{2} A+ A{3}) -> A{7,}
-CREATE TEMP VIEW v_opt_merge_complex AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A A{2} A+ A{3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_merge_complex');  -- original: (a a{2} a+ a{3})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_complex;  -- optimized: a{7,}
+--
+-- last_value IGNORE NULLS with reduced frame containing all NULLs
+-- Exercises ignorenulls_getfuncarginframe SEEK_TAIL out-of-frame path
+-- when notnull_relpos >= num_reduced_frame.
+--
+CREATE TEMP TABLE rpr_nullval (id INT, val INT);
+INSERT INTO rpr_nullval VALUES (1, 10), (2, NULL), (3, NULL), (4, 20);
 
--- Test: group merge ((A B) (A B)+) -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B) (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
+SELECT id, val,
+       last_value(val) IGNORE NULLS OVER w AS lv_ignull,
+       count(*) OVER w AS cnt
+FROM rpr_nullval
+WINDOW w AS (
+  ORDER BY id
+  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  AFTER MATCH SKIP PAST LAST ROW
+  PATTERN (A B+)
+  DEFINE
+    A AS val IS NOT NULL,
+    B AS val IS NULL
 );
-SELECT pg_get_viewdef('v_opt_merge_group');  -- original: ((a b) (a b)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group;  -- expected: (a b){2,}
 
--- Test: group merge A B (A B)+ -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_group2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group2');  -- original: (a b (a b)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group2;  -- expected: (a b){2,}
+--
+-- NULL handling
+--
 
--- Test: group merge (A B) (A B)+ (A B) -> (A B){3,}
-CREATE TEMP VIEW v_opt_merge_group3 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B) (A B)+ (A B))
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_merge_group3');  -- original: ((a b) (a b)+ (a b))
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group3;  -- expected: (a b){3,}
+CREATE TEMP TABLE stock_null (company TEXT, tdate DATE, price INTEGER);
+INSERT INTO stock_null VALUES ('c1', '2023-07-01', 100);
+INSERT INTO stock_null VALUES ('c1', '2023-07-02', NULL);  -- NULL in middle
+INSERT INTO stock_null VALUES ('c1', '2023-07-03', 200);
+INSERT INTO stock_null VALUES ('c1', '2023-07-04', 150);
 
--- Test: group merge A B A B (A B)+ A B A B -> (A B){5,}
-CREATE TEMP VIEW v_opt_merge_group4 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B A B (A B)+ A B A B)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
+SELECT company, tdate, price, count(*) OVER w AS match_count
+FROM stock_null
+WINDOW w AS (
+  PARTITION BY company
+  ORDER BY tdate
+  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+  PATTERN (START UP DOWN)
+  DEFINE START AS TRUE, UP AS price > PREV(price), DOWN AS price <
+PREV(price)
 );
-SELECT pg_get_viewdef('v_opt_merge_group4');  -- original: (a b a b (a b)+ a b a b)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group4;  -- expected: (a b){5,}
 
--- Test: group merge C A B (A B)+ A B C -> C (A B){3,} C
-CREATE TEMP VIEW v_opt_merge_group5 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (C A B (A B)+ A B C)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100,
-  C AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_merge_group5');  -- original: (c a b (a b)+ a b c)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_group5;  -- expected: c (a b){3,} c
+-- Consecutive NULLs: PREV navigates through NULL values
+CREATE TEMP TABLE rpr_consec_null (id INT, val INT);
+INSERT INTO rpr_consec_null VALUES
+ (1, 100), (2, NULL), (3, NULL), (4, NULL), (5, 200), (6, 300);
 
--- Test: consecutive GROUP merge (A B)+ (A B)+ -> (A B){2,}
-CREATE TEMP VIEW v_opt_merge_consec_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- PREV(val) IS NULL succeeds for both null_slot (first row) and actual NULL
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_consec_null
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B)+ (A B)+)
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+ C)
  DEFINE
-  A AS price > 100,
-  B AS price <= 100
+  A AS val IS NULL,
+  B AS val IS NULL AND PREV(val) IS NULL,
+  C AS val IS NOT NULL
 );
-SELECT pg_get_viewdef('v_opt_merge_consec_group');  -- original: ((a b)+ (a b)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_consec_group;  -- expected: (a b){2,}
 
--- Test: consecutive GROUP merge with different quantifiers (A B){2} (A B){3} -> (A B){5}
-CREATE TEMP VIEW v_opt_merge_consec_group2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
+-- NEXT(val) through consecutive NULLs
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_consec_null
+WINDOW w AS (
+ ORDER BY id
  ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A B){2} (A B){3})
+ AFTER MATCH SKIP PAST LAST ROW
+ PATTERN (A B+ C)
  DEFINE
-  A AS price > 100,
-  B AS price <= 100
+  A AS val IS NOT NULL,
+  B AS val IS NULL AND NEXT(val) IS NULL,
+  C AS val IS NULL AND NEXT(val) IS NOT NULL
 );
-SELECT pg_get_viewdef('v_opt_merge_consec_group2');  -- original: ((a b){2} (a b){3})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_merge_consec_group2;  -- expected: (a b){5}
 
--- Test {n} quantifier display
-CREATE TEMP VIEW v_quantifier_n AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A{3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_quantifier_n');
-
--- Test {n,} quantifier display
-CREATE TEMP VIEW v_quantifier_n_plus AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A{2,})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_quantifier_n_plus');
-
--- Test: flatten nested SEQ (A (B C)) -> A B C
-CREATE TEMP VIEW v_opt_flatten_seq AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A (B C))
- DEFINE
-  A AS price > 100,
-  B AS price > 150,
-  C AS price < 150
-);
-SELECT pg_get_viewdef('v_opt_flatten_seq');  -- original: (a (b c))
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_flatten_seq;  -- optimized: a b c
-
--- Test: flatten nested ALT (A | (B | C)) -> (A | B | C)
-CREATE TEMP VIEW v_opt_flatten_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | (B | C))+)
- DEFINE
-  A AS price > 200,
-  B AS price > 100,
-  C AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_flatten_alt');  -- original: ((a | (b | c))+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_flatten_alt;  -- optimized: ((a | b | c))+
-
--- Test: unwrap GROUP{1,1} ((A)) -> A
-CREATE TEMP VIEW v_opt_unwrap_group AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A)))
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_unwrap_group');  -- original: (((a)))
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_unwrap_group;  -- optimized: a
-
--- Test: quantifier multiplication (A{2}){3} -> A{6}
-CREATE TEMP VIEW v_opt_quant_mult AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2}){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult');  -- original: ((a{2}){3})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult;  -- optimized: a{6}
-
--- Test: quantifier multiplication (A{2,4}){3} -> A{6,12}
-CREATE TEMP VIEW v_opt_quant_mult_range AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2,4}){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_range');  -- original: ((a{2,4}){3})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_range;  -- optimized: a{6,12}
-
--- Test: quantifier multiplication blocked (A{2}){3,5} -> no change
--- outer range with child exact > 1 causes gaps (6,8,10 not 6,7,8,9,10)
-CREATE TEMP VIEW v_opt_quant_mult_range2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A{2}){3,5})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_range2');  -- original: ((a{2}){3,5})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_range2;  -- NOT optimized: (a{2}){3,5}
-
--- Test: quantifier multiplication blocked by INF (A+){3} -> no change
-CREATE TEMP VIEW v_opt_quant_mult_inf AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A+){3})
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_quant_mult_inf');  -- original: ((a+){3})
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_quant_mult_inf;  -- no multiply: (a+){3}
-
--- Test: unwrap single-item ALT after duplicate removal (A | A) -> A
-CREATE TEMP VIEW v_opt_unwrap_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN ((A | A)+)
- DEFINE
-  A AS price > 100
-);
-SELECT pg_get_viewdef('v_opt_unwrap_alt');  -- original: ((a | a)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_unwrap_alt;  -- optimized: a+
-
--- Test: GROUP{1,1} to SEQ with flatten ((A B)(C D)) -> A B C D
-CREATE TEMP VIEW v_opt_group_to_seq AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A B)(C D)))
- DEFINE
-  A AS price > 200,
-  B AS price > 150,
-  C AS price > 100,
-  D AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_group_to_seq');  -- original: (((a b)(c d)))
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_group_to_seq;  -- optimized: a b c d
-
--- Test: combined consecutive GROUP + prefix merge A B (A B)+ (A B)+ -> (A B){3,}
-CREATE TEMP VIEW v_opt_combined_merge AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (A B (A B)+ (A B)+)
- DEFINE
-  A AS price > 100,
-  B AS price <= 100
-);
-SELECT pg_get_viewdef('v_opt_combined_merge');  -- original: (a b (a b)+ (a b)+)
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_combined_merge;  -- expected: (a b){3,}
-
--- Test: nested ALT pattern - bug reproduction
-CREATE TEMP VIEW v_opt_nested_alt AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A B) | C) D | A B C)
- DEFINE
-  A AS price <= 100,
-  B AS price <= 150,
-  C AS price <= 200,
-  D AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_nested_alt');
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_nested_alt;
-
--- Test: nested ALT with unbounded - A+ inside
-CREATE TEMP VIEW v_opt_nested_alt2 AS
-SELECT company, tdate, price, count(*) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (((A+ B) | C) D | A B C)
- DEFINE
-  A AS price <= 100,
-  B AS price <= 150,
-  C AS price <= 200,
-  D AS price > 200
-);
-SELECT pg_get_viewdef('v_opt_nested_alt2');
-EXPLAIN (COSTS OFF) SELECT * FROM v_opt_nested_alt2;
-
---
--- Error cases
---
-
--- row pattern definition variable name must not appear more than once
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price),
-  UP AS price > PREV(price)
-);
-
--- subqueries in DEFINE clause are not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START LOWPRICE)
- DEFINE
-  START AS TRUE,
-  LOWPRICE AS price < (SELECT 100)
-);
-
--- aggregates in DEFINE clause are not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START LOWPRICE)
- DEFINE
-  START AS TRUE,
-  LOWPRICE AS price < count(*)
-);
-
--- FRAME must start at current row when row pattern recognition is used
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-
--- EXCLUDE is not permitted
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- EXCLUDE CURRENT ROW
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-
--- SEEK is not supported
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- SEEK
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(price),
-  DOWN AS price < PREV(price)
-);
-
--- PREV's argument must have at least 1 column reference
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP+ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-
--- Unsupported quantifier
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP~ DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-
--- PREV(1) missing column reference (error)
-SELECT company, tdate, price, first_value(price) OVER w, last_value(price) OVER w
- FROM stock
- WINDOW w AS (
- PARTITION BY company
- ORDER BY tdate
- ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
- AFTER MATCH SKIP TO NEXT ROW
- INITIAL
- PATTERN (START UP+? DOWN+)
- DEFINE
-  START AS TRUE,
-  UP AS price > PREV(1),
-  DOWN AS price < PREV(1)
-);
-
--- Maximum pattern variables is 251 (RPR_VARID_MAX)
-
--- Error: 252 variables exceeds limit of 251
-DO $$
-DECLARE
-    pattern_vars text;
-    define_vars text;
-    query text;
-BEGIN
-    SELECT string_agg('v' || lpad(i::text, 3, '0'), ' '),
-           string_agg('v' || lpad(i::text, 3, '0') || ' AS TRUE', ', ')
-    INTO pattern_vars, define_vars
-    FROM generate_series(1, 252) i;
-
-    query := format('SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (%s)
-        DEFINE %s)', pattern_vars, define_vars);
-
-    EXECUTE query;
-END;
-$$;
-
--- Error: 253 variables exceeds limit of 251
-DO $$
-DECLARE
-    pattern_vars text;
-    define_vars text;
-    query text;
-BEGIN
-    SELECT string_agg('v' || lpad(i::text, 3, '0'), ' '),
-           string_agg('v' || lpad(i::text, 3, '0') || ' AS TRUE', ', ')
-    INTO pattern_vars, define_vars
-    FROM generate_series(1, 253) i;
-
-    query := format('SELECT * FROM (SELECT 1 AS x) t WINDOW w AS (
-        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-        PATTERN (%s)
-        DEFINE %s)', pattern_vars, define_vars);
-
-    EXECUTE query;
-END;
-$$;
-
-    CREATE TEMP TABLE stock_null (company TEXT, tdate DATE, price INTEGER);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-01', 100);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-02', NULL);  -- NULL in middle
-    INSERT INTO stock_null VALUES ('c1', '2023-07-03', 200);
-    INSERT INTO stock_null VALUES ('c1', '2023-07-04', 150);
-
-    SELECT company, tdate, price, count(*) OVER w AS match_count
-    FROM stock_null
-    WINDOW w AS (
-      PARTITION BY company
-      ORDER BY tdate
-      ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-      PATTERN (START UP DOWN)
-      DEFINE START AS TRUE, UP AS price > PREV(price), DOWN AS price <
-PREV(price)
-    );
-
-
--- Overlapping match tests (requires multi-context for correct behavior)
--- Using array flags: 'X' = ANY(flags) for multi-TRUE support
-
--- Test 1: A B C D E | B C D | C D E F - three overlapping patterns
--- Different end points: B C D (4), A B C D E (5), C D E F (6)
-WITH test_overlap1 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E']),
-        (6, ARRAY['F'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | B C D | C D E F)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags),
-        F AS 'F' = ANY(flags)
-);
-
-WITH test_overlap1 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E']),
-        (6, ARRAY['F'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B C D E | B C D | C D E F)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags),
-        F AS 'F' = ANY(flags)
-);
--- PAST LAST: only one match
--- TO NEXT ROW with multi-context: three matches
---   Row 1: A B C D E (1-5)
---   Row 2: B C D (2-4) <- ends first!
---   Row 3: C D E F (3-6) <- ends last!
-
--- Test 1b: Longer pattern FAILS, shorter pattern should survive
--- Pattern: A+ B C D E | B+ C
--- A+ B C D E fails (no E found in sequence)
--- B+ C matches at rows 2-3
--- Result: match 2-3 (B+ C)
-WITH test_overlap1b AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap1b
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A+ B C D E | B+ C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
-
--- Test 2: A B+ C | B+ D - long B sequence with different endings
-WITH test_overlap2 AS (
-    SELECT * FROM (VALUES
-        (1,  ARRAY['A']),
-        (2,  ARRAY['B']),
-        (3,  ARRAY['B']),
-        (4,  ARRAY['B']),
-        (5,  ARRAY['B']),
-        (6,  ARRAY['C']),
-        (7,  ARRAY['B']),
-        (8,  ARRAY['B']),
-        (9,  ARRAY['B']),
-        (10, ARRAY['D'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_overlap2
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B+ C | B+ D)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
--- Current result (correct):
---   Row 1: A B+ C (1-6)
---   Row 7-9: B+ D (7-10, 8-10, 9-10)
--- Note: Row 2-6 cannot match B+ D because Row 6 is C, not D
--- With absorption: 8-10 and 9-10 would be absorbed by 7-10 (earlier context covers later)
-
--- Test 3: Greedy quantifier with late failure - A B C+ D | A B
--- Pattern expects D after C+, but E comes instead ("betrayal")
-WITH test_betrayal AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['C']),
-        (5, ARRAY['C']),
-        (6, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_betrayal
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C+ D | A B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
--- A B C+ D fails at Row 6 (E instead of D)
--- Question: Does it fallback to A B (1-2)?
-
--- Test 4: Lexical Order test - A B C | A B C D E
--- SQL standard: first matching alternative wins
-WITH test_lexical AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_lexical
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C | A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
--- SQL standard Lexical Order: A B C (1-3) wins (first alternative)
-
--- Test 4b: Reversed pattern order - A B C D E | A B C
-WITH test_lexical2 AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_lexical2
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
--- SQL standard Lexical Order: A B C D E (1-5) wins (first alternative)
-
--- Test 5: Multiple TRUE in single row (overlapping pattern variables)
--- Each row matches multiple DEFINE conditions simultaneously
-WITH test_multi_true AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','B']),        -- A and B both TRUE
-        (2, ARRAY['B','C']),        -- B and C both TRUE
-        (3, ARRAY['C','D']),        -- C and D both TRUE
-        (4, ARRAY['D','E']),        -- D and E both TRUE
-        (5, ARRAY['E','_'])         -- E only
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_multi_true
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
--- Row 1: A=T, B=T -> matches A
--- Row 2: B=T, C=T -> matches B
--- Row 3: C=T, D=T -> matches C
--- Row 4: D=T, E=T -> matches D
--- Row 5: E=T      -> matches E
--- Result: match 1-5 (A B C D E)
-
--- Test 6: Diagonal pattern with multi-TRUE (shifted overlap)
-WITH test_diagonal AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['B','A']),
-        (3, ARRAY['C','B']),
-        (4, ARRAY['D','C']),
-        (5, ARRAY['_','D'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_diagonal
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B C D)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
--- Possible matches:
---   Start Row 1: A(1) B(2) C(3) D(4) -> 1-4
---   Start Row 2: A(2) B(3) C(4) D(5) -> 2-5 (because Row 2 has A too!)
-
--- ===================================================================
--- Context Absorption Tests
--- ===================================================================
-
--- Test absorption 1: Basic A+ pattern - later contexts absorbed by earlier
-WITH test_absorb_basic AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['A']),
-        (5, ARRAY['B'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_basic
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+)
-    DEFINE A AS 'A' = ANY(flags)
-);
--- Pattern A+ is absorbable (unbounded first element, only one unbounded)
--- 4 matches: (1-4, 2-4, 3-4, 4-4)
-
--- Test absorption 2: A+ B pattern - absorption with fixed suffix
-WITH test_absorb_suffix AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_suffix
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+ B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- Pattern A+ B is absorbable (A+ unbounded first, B bounded suffix)
--- All potential matches end at same row (row 4 with B)
--- 3 matches: (1-4, 2-4, 3-4)
-
--- Test absorption 3: Per-branch absorption with ALT (B+ C | B+ D)
-WITH test_absorb_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['B']),
-        (2, ARRAY['B']),
-        (3, ARRAY['B']),
-        (4, ARRAY['D']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (B+ C | B+ D)
-    DEFINE
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags)
-);
--- Both branches B+ C and B+ D are absorbable (B+ unbounded first)
--- B+ D branch matches: 3 matches (1-4, 2-4, 3-4)
-
--- Test absorption 4: Non-absorbable pattern (A B+ - unbounded not first)
-WITH test_no_absorb AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['B']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_no_absorb
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A B+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- Pattern A B+ is NOT absorbable (A bounded first, B+ unbounded but not first)
--- Only Row 1 can start match (only row with A), so only one match: 1-4
-
--- Test absorption 5: GROUP merge enables absorption
-WITH test_absorb_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['A']),
-        (6, ARRAY['B']),
-        (7, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_absorb_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN ((A B) (A B)+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- Pattern optimized: (A B) (A B)+ -> (A B){2,}
--- 2 matches: 1-6 (3 reps), 3-6 (2 reps)
-
--- Test absorption 6: Multiple unbounded - first element unbounded enables absorption
-WITH test_multi_unbounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['B']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM test_multi_unbounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP TO NEXT ROW
-    PATTERN (A+ B+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- 2 matches: 1-4, 2-4 (same endpoint 4)
-
--- ============================================
--- Jacob's RPR Patterns (from jacob branch)
--- ============================================
-
--- Test: A? (optional, greedy)
-WITH jacob_optional AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_optional
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A?)
-    DEFINE A AS 'A' = ANY(flags)
-);
--- Expected: 1-1 (matches A)
-
--- Test: A{2} (exact count)
-WITH jacob_exact AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_exact
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{2})
-    DEFINE A AS 'A' = ANY(flags)
-);
--- Expected: 1-2
-
--- Test: A{1,3} (bounded range, greedy)
-WITH jacob_bounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['A']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_bounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{1,3})
-    DEFINE A AS 'A' = ANY(flags)
-);
--- Expected: 1-3 (greedy takes max), then 4-4
-
--- Test: A | B (simple alternation)
-WITH jacob_simple_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_simple_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A | B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- Expected: 1-1 (A), 2-2 (B)
-
--- Test: A | B | C (three-way alternation)
-WITH jacob_three_alt AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['B']),
-        (2, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_three_alt
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A | B | C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
--- Expected: 1-1 (B)
-
--- Test: A B C (concatenation)
-WITH jacob_concat AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_concat
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
--- Expected: 1-3
-
--- Test: A B? C (optional middle)
-WITH jacob_optional_mid AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['C']),
-        (3, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_optional_mid
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B? C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
--- Expected: 1-2 (A C, B skipped)
-
--- Test: (A B){2} (nested group with quantifier)
-WITH jacob_nested_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['B']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_nested_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A B){2})
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
--- Expected: 1-4 (A B A B)
-
--- Test: (A){3} (quantifier on grouped single element)
-WITH jacob_group_quant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['A']),
-        (3, ARRAY['A']),
-        (4, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_group_quant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A){3})
-    DEFINE A AS 'A' = ANY(flags)
-);
--- Expected: 1-3
-
--- Test: A B C | A B C D E (lexical order - first alt wins)
-WITH jacob_lex_first AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_lex_first
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C | A B C D E)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
--- Expected: 1-3 (A B C wins by lexical order)
-
--- Test: A B C D E | A B C (lexical order - longer first wins)
-WITH jacob_lex_long AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['C']),
-        (4, ARRAY['D']),
-        (5, ARRAY['E'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_lex_long
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B C D E | A B C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags),
-        D AS 'D' = ANY(flags),
-        E AS 'E' = ANY(flags)
-);
--- Expected: 1-5 (A B C D E wins by lexical order)
-
--- ============================================
--- Alternation with quantifiers (BUG cases from Jacob's tests)
--- ============================================
-
--- Test: (A | B)+ C - alternation inside quantified group followed by C
-WITH jacob_alt_quant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['B']),
-        (3, ARRAY['A']),
-        (4, ARRAY['C'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_alt_quant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN ((A | B)+ C)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
--- Expected: 1-4 (A B A C)
-
--- Test: ((A | B) C)+ - alternation inside group with outer quantifier
-WITH jacob_alt_group AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A']),
-        (2, ARRAY['C']),
-        (3, ARRAY['B']),
-        (4, ARRAY['C']),
-        (5, ARRAY['X'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_alt_group
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (((A | B) C)+)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags),
-        C AS 'C' = ANY(flags)
-);
--- Expected: 1-4 (A C B C)
-
--- ============================================
--- RELUCTANT quantifiers
--- ============================================
-
--- Test: A+? B (reluctant) - B available at row 3, A exits early
-WITH jacob_reluctant AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['A','_']),
-        (3, ARRAY['A','B']),
-        (4, ARRAY['B','_'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_reluctant
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A+? B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
-
--- Test: A{1,3}? B (reluctant bounded) - same data, bounded quantifier
-WITH jacob_reluctant_bounded AS (
-    SELECT * FROM (VALUES
-        (1, ARRAY['A','_']),
-        (2, ARRAY['A','_']),
-        (3, ARRAY['A','B']),
-        (4, ARRAY['B','_'])
-    ) AS t(id, flags)
-)
-SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
-FROM jacob_reluctant_bounded
-WINDOW w AS (
-    ORDER BY id
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A{1,3}? B)
-    DEFINE
-        A AS 'A' = ANY(flags),
-        B AS 'B' = ANY(flags)
-);
-
--- ============================================
--- Nested quantifiers (pathological patterns)
--- ============================================
--- These patterns previously caused segfault or infinite loop.
--- Now they are either optimized at compile time or handled safely at runtime.
-
--- Test: (A*)* - nested unbounded quantifiers (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A*)*)
-    DEFINE A AS TRUE
-);
-
--- Test: (A*)+ - inner nullable, outer requires one (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A*)+)
-    DEFINE A AS TRUE
-);
-
--- Test: (A+)* - outer nullable (optimized to A*)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A+)*)
-    DEFINE A AS TRUE
-);
-
--- Test: (A+)+ - both require match (optimized to A+)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A+)+)
-    DEFINE A AS TRUE
-);
-
--- Test: (A* B*)* - complex nested pattern (runtime protection)
--- Not optimized but handled safely by empty-match loop prevention
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 5) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((A* B*)*)
-    DEFINE A AS TRUE, B AS TRUE
-);
-
--- Test: (((A)*)*)*  - triple nested (optimized through recursive optimization)
-SELECT v, count(*) OVER w AS c
-FROM (SELECT generate_series(1, 3) v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    INITIAL
-    PATTERN ((((A)*)*)*)
-    DEFINE A AS TRUE
-);
+DROP TABLE rpr_consec_null;
diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql
index 97b62a73a0e..da28de1a8f9 100644
--- a/src/test/regress/sql/rpr_base.sql
+++ b/src/test/regress/sql/rpr_base.sql
@@ -1050,7 +1050,7 @@ WINDOW w AS (
 );
 -- Expected: ERROR: syntax error at or near "*"
 
--- ? ? (invalid combination)
+-- ? ? (parsed as ?? reluctant quantifier)
 SELECT COUNT(*) OVER w
 FROM rpr_reluctant
 WINDOW w AS (
@@ -1533,7 +1533,17 @@ SELECT pg_get_viewdef('rpr_serial_v8'::regclass);
 
 DROP VIEW rpr_serial_v8;
 
-DROP TABLE rpr_serial;
+-- Reluctant {1}? quantifier deparse through ruleutils
+CREATE VIEW rpr_quant_reluctant_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{1}? B)
+             DEFINE A AS val > 0, B AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_reluctant_v'::regclass);
+DROP VIEW rpr_quant_reluctant_v;
 
 -- Materialized view (if supported)
 
@@ -1560,6 +1570,40 @@ SELECT * FROM rpr_mview_v1 ORDER BY id;
 DROP MATERIALIZED VIEW rpr_mview_v1;
 DROP TABLE rpr_mview;
 
+-- CREATE TABLE AS SELECT with RPR
+CREATE TABLE rpr_ctas (id INT, val INT);
+INSERT INTO rpr_ctas VALUES (1, 10), (2, 20), (3, 15), (4, 25);
+
+CREATE TABLE rpr_ctas_result AS
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_ctas
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE A AS TRUE, B AS val > PREV(val)
+);
+SELECT * FROM rpr_ctas_result ORDER BY id;
+
+-- INSERT INTO ... SELECT with RPR
+CREATE TABLE rpr_insert_target (id INT, val INT, cnt BIGINT);
+INSERT INTO rpr_insert_target
+SELECT id, val, count(*) OVER w
+FROM rpr_ctas
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE A AS TRUE, B AS val > PREV(val)
+);
+SELECT * FROM rpr_insert_target ORDER BY id;
+
+DROP TABLE rpr_ctas_result;
+DROP TABLE rpr_insert_target;
+DROP TABLE rpr_ctas;
+
 -- Prepared statements (tests outfuncs.c / readfuncs.c)
 
 CREATE TABLE rpr_prep (id INT, val INT);
@@ -1822,6 +1866,32 @@ SELECT pg_get_viewdef('rpr_multiwin_v'::regclass);
 DROP VIEW rpr_multiwin_v;
 DROP TABLE rpr_multiwin;
 
+-- {n} quantifier display in view
+CREATE VIEW rpr_quant_n_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{3})
+             DEFINE A AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_n_v'::regclass);
+DROP VIEW rpr_quant_n_v;
+
+-- {n,} quantifier display in view
+CREATE VIEW rpr_quant_n_plus_v AS
+SELECT id, val, count(*) OVER w
+FROM rpr_serial
+WINDOW w AS (ORDER BY id
+             ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             INITIAL
+             PATTERN (A{2,})
+             DEFINE A AS val > 0);
+SELECT pg_get_viewdef('rpr_quant_n_plus_v'::regclass);
+DROP VIEW rpr_quant_n_plus_v;
+
+DROP TABLE rpr_serial;
+
 -- ============================================================
 -- Error Cases Tests
 -- ============================================================
@@ -1901,6 +1971,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS A.val > 0
 );
+-- Expected: ERROR: pattern variable qualified column reference "a.val" is not supported
 
 -- PATTERN-only variable qualified name: not supported even without DEFINE entry
 SELECT COUNT(*) OVER w
@@ -1911,6 +1982,7 @@ WINDOW w AS (
     PATTERN (A+ B+)
     DEFINE A AS B.val > 0
 );
+-- Expected: ERROR: pattern variable qualified column reference "b.val" is not supported
 
 -- DEFINE-only variable qualified name: still a pattern variable, not a range variable
 SELECT COUNT(*) OVER w
@@ -1921,6 +1993,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS val > 0, B AS B.val > 0
 );
+-- Expected: ERROR: pattern variable qualified column reference "b.val" is not supported
 
 -- FROM-clause range variable qualified name: not allowed (prohibited by §6.5)
 SELECT COUNT(*) OVER w
@@ -1931,6 +2004,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS rpr_err.val > 0
 );
+-- Expected: ERROR: range variable qualified column reference "rpr_err.val" is not allowed
 
 -- Semantic errors
 
@@ -1965,7 +2039,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS COUNT(*) > 0
 );
--- Expected: ERROR or works depending on implementation
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 
 -- Subquery in DEFINE (NOT SUPPORTED)
 SELECT COUNT(*) OVER w
@@ -2108,6 +2182,13 @@ SELECT COUNT(*) OVER w FROM rpr_plan
 WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
              PATTERN (A ((B) (C))) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
 
+-- Data execution: SEQ flatten produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP TO NEXT ROW
+             PATTERN (A ((B) (C))) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
+
 -- ALT flatten: (A | (B | C))+ -> (a | b | c)+
 EXPLAIN (COSTS OFF)
 SELECT COUNT(*) OVER w FROM rpr_plan
@@ -2120,6 +2201,13 @@ SELECT COUNT(*) OVER w FROM rpr_plan
 WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
              PATTERN ((A | B | A)+) DEFINE A AS val <= 50, B AS val > 50);
 
+-- Data execution: ALT dedup produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP PAST LAST ROW
+             PATTERN ((A | B | A)+) DEFINE A AS val <= 50, B AS val > 50);
+
 -- Quantifier multiply: (A{2}){3} -> a{6}
 EXPLAIN (COSTS OFF)
 SELECT COUNT(*) OVER w FROM rpr_plan
@@ -2305,6 +2393,13 @@ SELECT COUNT(*) OVER w FROM rpr_plan
 WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
              PATTERN ((A | B | C)) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
 
+-- Data execution: GROUP unwrap produces correct results
+SELECT id, val, count(*) OVER w AS cnt
+FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             AFTER MATCH SKIP TO NEXT ROW
+             PATTERN ((A | B | C)) DEFINE A AS val <= 30, B AS val <= 60, C AS val > 60);
+
 -- Reluctant optimization bypass: VAR merge
 -- A+? A stays as a+? a (greedy A+ A merges to a{2,})
 EXPLAIN (COSTS OFF)
@@ -2368,6 +2463,62 @@ SELECT COUNT(*) OVER w FROM rpr_plan
 WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
              AFTER MATCH SKIP PAST LAST ROW PATTERN (A+?) DEFINE A AS val > 0);
 
+-- Duplicate GROUP removal: ((A | B)+ | (A | B)+) -> (a | b)+
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN ((A | B)+ | (A | B)+) DEFINE A AS val <= 50, B AS val > 50);
+
+-- Consecutive VAR merge with zero-min: A* A+ -> a+
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A* A+) DEFINE A AS val > 0);
+
+-- Consecutive VAR merge (4-element): A A{2} A+ A{3} -> a{7,}
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A A{2} A+ A{3}) DEFINE A AS val > 0);
+
+-- PREFIX+SUFFIX merge (5-way): A B A B (A B)+ A B A B -> (a b){5,}
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (A B A B (A B)+ A B A B)
+             DEFINE A AS val <= 50, B AS val > 50);
+
+-- Unwrap single-item ALT after dedup: (A | A)+ -> a+
+-- ALT dedup reduces to single-item, then GROUP unwrap
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN ((A | A)+) DEFINE A AS val > 0);
+
+-- GROUP{1,1} to SEQ with flatten: ((A B)(C D)) -> a b c d
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A B)(C D)))
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+
+-- Nested ALT pattern: ((A B) | C) D | A B C
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A B) | C) D | A B C)
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+
+-- Nested ALT with unbounded: ((A+ B) | C) D | A B C
+EXPLAIN (COSTS OFF)
+SELECT COUNT(*) OVER w FROM rpr_plan
+WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+             PATTERN (((A+ B) | C) D | A B C)
+             DEFINE A AS val <= 25, B AS val > 25 AND val <= 50,
+                    C AS val > 50 AND val <= 75, D AS val > 75);
+
 -- ============================================================
 -- Absorption Flag Display Tests
 -- ============================================================
@@ -2450,6 +2601,19 @@ SELECT COUNT(*) OVER w FROM rpr_plan
 WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND 10 FOLLOWING
              AFTER MATCH SKIP PAST LAST ROW PATTERN (A+) DEFINE A AS val > 0);
 
+-- Reluctant {1}? quantifier deparse
+-- A{1}? is a reluctant {1,1} quantifier.  The deparse code must
+-- output "{1}" explicitly to disambiguate from a bare "?" quantifier
+-- (which would mean {0,1}).
+EXPLAIN (COSTS OFF) SELECT count(*) OVER w
+FROM rpr_plan
+WINDOW w AS (
+    ORDER BY val
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    PATTERN (A{1}? B)
+    DEFINE A AS val > 0, B AS val > 0
+);
+
 -- ============================================================
 -- Absorption Analysis Tests
 -- ============================================================
@@ -2871,7 +3035,8 @@ WINDOW w AS (
 )
 GROUP BY category
 ORDER BY category;
--- Expected: ERROR (GROUP BY with window RPR not supported)
+-- Expected: ERROR: syntax error at or near "GROUP"
+-- (GROUP BY after WINDOW clause is not valid SQL syntax)
 
 -- ============================================================
 -- Subquery and CTE Tests
@@ -3243,7 +3408,8 @@ INSERT INTO rpr_sort VALUES
     (1, 'A', 30), (2, 'B', 20), (3, 'A', 10),
     (4, 'B', 40), (5, 'A', 50), (6, 'B', 60);
 
--- RPR with GROUP BY
+-- RPR with GROUP BY (aggregate in DEFINE → ERROR before GROUP BY interaction)
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 
 SELECT category,
        COUNT(*) as group_cnt,
@@ -3259,7 +3425,8 @@ WINDOW w AS (
 )
 ORDER BY category;
 
--- RPR with HAVING
+-- RPR with HAVING (same aggregate-in-DEFINE error)
+-- Expected: ERROR: aggregate functions are not allowed in DEFINE
 
 SELECT category,
        COUNT(*) as group_cnt,
@@ -3771,7 +3938,3 @@ FROM (SELECT id, val,
 ) s;
 
 DROP TABLE rpr_plan;
-
--- ============================================================
--- End of rpr_base.sql
--- ============================================================
diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql
index d017a2292d2..4bb49650bb7 100644
--- a/src/test/regress/sql/rpr_explain.sql
+++ b/src/test/regress/sql/rpr_explain.sql
@@ -6,8 +6,9 @@
 -- This test suite validates EXPLAIN output for RPR queries,
 -- including NFA statistics shown in EXPLAIN ANALYZE:
 --   - NFA States: peak, total, merged
---   - NFA Contexts: peak, total, absorbed, skipped
+--   - NFA Contexts: peak, total, pruned
 --   - NFA: matched (len min/max/avg), mismatched (len min/max/avg)
+--   - NFA: absorbed (len min/max/avg), skipped (len min/max/avg)
 --   - Pattern deparse formatting
 --   - Multiple output formats (text, JSON, XML)
 --
@@ -310,7 +311,7 @@ WINDOW w AS (
 );');
 DROP VIEW rpr_v;
 
--- Grouped pattern with quantifier - state merging
+-- Grouped pattern with quantifier - state count with grouping
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
@@ -428,7 +429,7 @@ WINDOW w AS (
 );');
 DROP VIEW rpr_v;
 
--- High state merging - alternation with plus quantifier
+-- High state count - alternation with plus quantifier
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
@@ -499,7 +500,7 @@ WINDOW w AS (
 DROP VIEW rpr_v;
 
 -- ============================================================
--- Context Statistics Tests (peak, total, absorbed, skipped)
+-- Context Statistics Tests (peak, total, pruned + absorbed/skipped)
 -- ============================================================
 
 -- Context absorption with unbounded quantifier at start
@@ -671,7 +672,7 @@ WINDOW w AS (
 );');
 DROP VIEW rpr_v;
 
--- Mix of short and long matches
+-- Uniform match length with mismatches from gap rows (v%20 = 11..15)
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
@@ -702,8 +703,8 @@ DROP VIEW rpr_v;
 -- Mismatch Length Statistics Tests
 -- ============================================================
 
--- Pattern that causes mismatches with length > 1
--- Mismatch happens when partial match fails after processing multiple rows
+-- Pattern with complete match every cycle: 0 mismatched
+-- A(1,2,3) B(4,5) C(6) repeats perfectly; X rows are pruned, not mismatched
 CREATE TEMP VIEW rpr_v AS
 SELECT count(*) OVER w
 FROM (
@@ -837,33 +838,6 @@ WINDOW w AS (
 )');
 DROP VIEW rpr_v;
 
--- ============================================================
--- XML Format Tests
--- ============================================================
-
--- XML format output
-CREATE TEMP VIEW rpr_v AS
-SELECT count(*) OVER w
-FROM generate_series(1, 30) AS s(v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B)
-    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
-);
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
-SELECT rpr_explain_filter('
-EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
-SELECT count(*) OVER w
-FROM generate_series(1, 30) AS s(v)
-WINDOW w AS (
-    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
-    AFTER MATCH SKIP PAST LAST ROW
-    PATTERN (A B)
-    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
-)');
-DROP VIEW rpr_v;
-
 -- JSON format with mismatch statistics
 -- Pattern A B C expects 1,2,3 but gets 1,2,4 twice causing mismatches
 CREATE TEMP VIEW rpr_v AS
@@ -912,6 +886,33 @@ WINDOW w AS (
 )');
 DROP VIEW rpr_v;
 
+-- ============================================================
+-- XML Format Tests
+-- ============================================================
+
+-- XML format output
+CREATE TEMP VIEW rpr_v AS
+SELECT count(*) OVER w
+FROM generate_series(1, 30) AS s(v)
+WINDOW w AS (
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B)
+    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
+);
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT rpr_explain_filter('
+EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
+SELECT count(*) OVER w
+FROM generate_series(1, 30) AS s(v)
+WINDOW w AS (
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B)
+    DEFINE A AS v % 2 = 1, B AS v % 2 = 0
+)');
+DROP VIEW rpr_v;
+
 -- ============================================================
 -- Multiple Partitions Tests
 -- ============================================================
diff --git a/src/test/regress/sql/rpr_nfa.sql b/src/test/regress/sql/rpr_nfa.sql
index a3f01b60bc4..c0f4dfa1248 100644
--- a/src/test/regress/sql/rpr_nfa.sql
+++ b/src/test/regress/sql/rpr_nfa.sql
@@ -255,6 +255,119 @@ WINDOW w AS (
         A AS 'A' = ANY(flags)
 );
 
+-- Absorption with fixed suffix: A+ B
+WITH test_absorb_suffix AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_suffix
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- Per-branch absorption with ALT: B+ C | B+ D
+WITH test_absorb_alt AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['B']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B']),
+        (4, ARRAY['D']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_alt
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (B+ C | B+ D)
+    DEFINE
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+
+-- Non-absorbable: A B+ (unbounded not in first position)
+WITH test_no_absorb AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_no_absorb
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- GROUP merge enables absorption: (A B) (A B)+ optimized to (A B){2,}
+WITH test_absorb_group AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B']),
+        (7, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_absorb_group
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN ((A B) (A B)+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- Multiple unbounded: A+ B+ (first element unbounded enables absorption)
+WITH test_multi_unbounded AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['B']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_multi_unbounded
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
 -- ============================================================
 -- Context Lifecycle
 -- ============================================================
@@ -524,7 +637,8 @@ WINDOW w AS (
 );
 
 -- Optional reluctant group: (A B)?? C
--- nfa_advance_begin: reluctant prefers skip (0 iterations) over enter
+-- nfa_advance_begin: reluctant tries skip first, but skip path needs C
+-- at row 1 which is A → skip fails. Enter path succeeds: A(1) B(2) C(3).
 WITH test_optional_reluctant AS (
     SELECT * FROM (VALUES
         (1, ARRAY['A']),
@@ -571,6 +685,36 @@ WINDOW w AS (
         B AS 'B' = ANY(flags)
 );
 
+-- Reluctant optional group skip-to-FIN
+-- When a reluctant optional group's skip path reaches FIN, the group
+-- entry path is abandoned (nodeWindowAgg.c nfa_advance_begin).
+-- Pattern: C (A B)?? -- after C matches, the reluctant group (A B)??
+-- prefers to skip.  Skip goes to FIN (group is last element), so
+-- the match completes with just C.
+WITH test_begin_skip_fin AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['C']),
+        (2, ARRAY['A']),
+        (3, ARRAY['B']),
+        (4, ARRAY['C','A']),
+        (5, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_begin_skip_fin
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (C (A B)??)
+    DEFINE
+        C AS 'C' = ANY(flags),
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
 -- ============================================================
 -- Match Phase
 -- ============================================================
@@ -1198,6 +1342,50 @@ WINDOW w AS (
         B AS 'B' = ANY(flags)
 );
 
+-- A+? B (reluctant plus): exits A at first B availability
+-- (Same scenario as greedy-vs-reluctant comparison above; retained for
+-- standalone quantifier coverage alongside A{1,3}? and A{2,3}? below)
+WITH test_reluctant_plus AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['A','_']),
+        (3, ARRAY['A','B']),
+        (4, ARRAY['B','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_reluctant_plus
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A+? B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- A{1,3}? B (reluctant bounded): same data, bounded quantifier
+WITH test_reluctant_bounded AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['A','_']),
+        (3, ARRAY['A','B']),
+        (4, ARRAY['B','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_reluctant_bounded
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A{1,3}? B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
 -- ============================================================
 -- Pathological Pattern Runtime Protection
 -- ============================================================
@@ -1481,6 +1669,211 @@ WINDOW w AS (
         B AS 'B' = ANY(flags)
 );
 
+-- Overlapping match: A B C D E | B C D | C D E F (SKIP PAST LAST ROW)
+WITH test_overlap1 AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['E']),
+        (6, ARRAY['F'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C D E | B C D | C D E F)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags),
+        F AS 'F' = ANY(flags)
+);
+
+-- Same with SKIP TO NEXT ROW: three overlapping matches
+WITH test_overlap1 AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['E']),
+        (6, ARRAY['F'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B C D E | B C D | C D E F)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags),
+        F AS 'F' = ANY(flags)
+);
+
+-- Longer pattern fails, shorter survives: A+ B C D E | B+ C
+WITH test_overlap1b AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['D']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap1b
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A+ B C D E | B+ C)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags)
+);
+
+-- Long B sequence with different endings: A B+ C | B+ D
+WITH test_overlap2 AS (
+    SELECT * FROM (VALUES
+        (1,  ARRAY['A']),
+        (2,  ARRAY['B']),
+        (3,  ARRAY['B']),
+        (4,  ARRAY['B']),
+        (5,  ARRAY['B']),
+        (6,  ARRAY['C']),
+        (7,  ARRAY['B']),
+        (8,  ARRAY['B']),
+        (9,  ARRAY['B']),
+        (10, ARRAY['D'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_overlap2
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B+ C | B+ D)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+
+-- Greedy with late failure ("betrayal"): A B C+ D | A B
+WITH test_betrayal AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['C']),
+        (4, ARRAY['C']),
+        (5, ARRAY['C']),
+        (6, ARRAY['E'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_betrayal
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C+ D | A B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+
+-- Multiple TRUE per row: overlapping pattern variables
+WITH test_multi_true AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','B']),
+        (2, ARRAY['B','C']),
+        (3, ARRAY['C','D']),
+        (4, ARRAY['D','E']),
+        (5, ARRAY['E','_'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_multi_true
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B C D E)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags),
+        E AS 'E' = ANY(flags)
+);
+
+-- Diagonal pattern with shifted multi-TRUE overlap
+WITH test_diagonal AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A','_']),
+        (2, ARRAY['B','A']),
+        (3, ARRAY['C','B']),
+        (4, ARRAY['D','C']),
+        (5, ARRAY['_','D'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_diagonal
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A B C D)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags),
+        D AS 'D' = ANY(flags)
+);
+
+-- ((A | B) C)+ - alternation inside group with outer quantifier
+WITH test_alt_group AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['C']),
+        (3, ARRAY['B']),
+        (4, ARRAY['C']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_alt_group
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (((A | B) C)+)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags),
+        C AS 'C' = ANY(flags)
+);
+
 -- ============================================================
 -- Deep Nested Groups
 -- ============================================================
@@ -1625,6 +2018,55 @@ WINDOW w AS (
         C AS 'C' = ANY(flags)
 );
 
+-- (A B){2} - group with exact quantifier
+WITH test_group_exact AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['B']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['X'])
+    ) AS t(id, flags)
+)
+SELECT id, flags, first_value(id) OVER w AS match_start, last_value(id) OVER w AS match_end
+FROM test_group_exact
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN ((A B){2})
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- Nested END->END fast-forward
+-- When an inner group has a nullable body and count < min, the
+-- fast-forward path exits through the outer END, incrementing
+-- the outer group's count (nodeWindowAgg.c nfa_advance_end).
+-- Pattern: ((A?){2,3}){2,3} -- nested groups, neither collapses
+-- because the optimizer cannot safely multiply non-exact quantifiers.
+-- Data has no A rows, forcing all-empty iterations via fast-forward.
+WITH test_nested_ff AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['B']),
+        (2, ARRAY['B']),
+        (3, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_nested_ff
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (((A?){2,3}){2,3})
+    DEFINE
+        A AS 'A' = ANY(flags)
+);
+
 -- ============================================================
 -- SKIP Options (Runtime)
 -- ============================================================
@@ -1751,6 +2193,8 @@ ORDER BY mode, id;
 
 -- ============================================================
 -- INITIAL Mode (Runtime)
+-- Placeholder: INITIAL is not yet implemented (syntax error).
+-- Kept here so tests convert to runtime tests when implemented.
 -- ============================================================
 
 -- INITIAL mode (not yet supported - produces syntax error)
@@ -1917,6 +2361,62 @@ WINDOW w AS (
         B AS 'B' = ANY(flags)
 );
 
+-- N FOLLOWING + SKIP TO NEXT ROW: overlapping matches bounded by frame
+-- Row 1: frame [1,4], A(1-3) B(4) -> match
+-- Row 2: frame [2,5], A(2-3) B(4) -> match
+-- Row 3: frame [3,6], A(3) B(4) -> match
+-- Row 5: frame [5,6], A(5) B(6) -> match
+WITH test_n_skip_next AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_n_skip_next
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
+-- Frame exactly 1 row short of potential match
+-- From row 1: A A A B needs 4 rows but frame holds 3 -> no match
+-- From row 2: A A B fits in 3-row frame -> match
+WITH test_frame_one_short AS (
+    SELECT * FROM (VALUES
+        (1, ARRAY['A']),
+        (2, ARRAY['A']),
+        (3, ARRAY['A']),
+        (4, ARRAY['B']),
+        (5, ARRAY['A']),
+        (6, ARRAY['B'])
+    ) AS t(id, flags)
+)
+SELECT id, flags,
+       first_value(id) OVER w AS match_start,
+       last_value(id) OVER w AS match_end
+FROM test_frame_one_short
+WINDOW w AS (
+    ORDER BY id
+    ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING
+    AFTER MATCH SKIP TO NEXT ROW
+    PATTERN (A+ B)
+    DEFINE
+        A AS 'A' = ANY(flags),
+        B AS 'B' = ANY(flags)
+);
+
 -- ============================================================
 -- Special Partition Cases
 -- ============================================================
@@ -2567,7 +3067,8 @@ WINDOW w AS (
 
 -- (A?){0,3}: min=0, nullable inner.
 -- A never matches. A? matches empty, min=0 satisfied immediately.
--- Expected: empty match (match_start = match_end) for every row
+-- Per standard: empty match expected for every row.
+-- XXX: visited bitmap blocks empty iteration → no match (same as {2,3})
 WITH test_728_min0 AS (
     SELECT * FROM (VALUES
         (1, ARRAY['B']),
@@ -2590,7 +3091,8 @@ WINDOW w AS (
 
 -- (A?){1,3}: min=1, nullable inner.
 -- A never matches. Need 1 empty iteration to satisfy min=1.
--- Expected: empty match for every row
+-- Per standard: empty match expected for every row.
+-- XXX: visited bitmap blocks empty iteration → no match (same as {2,3})
 WITH test_728_min1 AS (
     SELECT * FROM (VALUES
         (1, ARRAY['B']),
@@ -2639,7 +3141,8 @@ WINDOW w AS (
 -- (A?){2,3} mixed: some rows match A, some don't
 -- Rows 1-2: A matches, greedy takes 2 → min satisfied
 -- Row 3: A doesn't match, needs 2 empty iterations for min=2
--- Expected: all rows produce matches
+-- XXX: Row 3 fails due to visited bitmap (same as pure empty {2,3})
+-- Row 4: A matches 1 real iter + 1 ff empty exit → match 4-4
 WITH test_728_min2_mixed AS (
     SELECT * FROM (VALUES
         (1, ARRAY['A']),
@@ -2773,4 +3276,3 @@ WINDOW w AS (
         A AS price > 100,
         B AS TRUE
 );
-
-- 
2.50.1 (Apple Git-155)


From f9c80e37bbc493737ed04d237abdd4115d5a412e Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 19:32:24 +0900
Subject: [PATCH 4/8] Keep RPR test objects for pg_upgrade/pg_dump testing


diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index 3383b242ef0..ae6d9c9b937 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -12,7 +12,7 @@
 --   Quantifiers Tests
 --   Navigation Functions Tests
 --   SKIP TO / INITIAL Tests
---   Serialization/Deserialization Tests
+--   Serialization/Deserialization Tests (objects kept for pg_upgrade/pg_dump)
 --   Error Cases Tests
 --
 -- Planner Layer:
@@ -1930,7 +1930,6 @@ SELECT pg_get_viewdef('rpr_serial_v1'::regclass);
    a AS (val > 0) );
 (1 row)
 
-DROP VIEW rpr_serial_v1;
 -- Complex pattern with alternation
 CREATE VIEW rpr_serial_v2 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1967,7 +1966,6 @@ SELECT pg_get_viewdef('rpr_serial_v2'::regclass);
    b AS (val <= 20) );
 (1 row)
 
-DROP VIEW rpr_serial_v2;
 -- Pattern with grouping and quantifiers
 CREATE VIEW rpr_serial_v3 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -2008,7 +2006,6 @@ SELECT pg_get_viewdef('rpr_serial_v3'::regclass);
    c AS (val <= 10) );
 (1 row)
 
-DROP VIEW rpr_serial_v3;
 -- All features combined
 CREATE VIEW rpr_serial_v4 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -2053,7 +2050,6 @@ SELECT pg_get_viewdef('rpr_serial_v4'::regclass);
    finish AS (val > 15) );
 (1 row)
 
-DROP VIEW rpr_serial_v4;
 -- Additional quantifiers for deparsing coverage
 -- ? quantifier (zero or one)
 CREATE VIEW rpr_serial_v5 AS
@@ -2091,7 +2087,6 @@ SELECT pg_get_viewdef('rpr_serial_v5'::regclass);
    b AS (val > 20) );
 (1 row)
 
-DROP VIEW rpr_serial_v5;
 -- {n,} quantifier (n or more)
 CREATE VIEW rpr_serial_v6 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -2127,7 +2122,6 @@ SELECT pg_get_viewdef('rpr_serial_v6'::regclass);
    a AS (val > 15) );
 (1 row)
 
-DROP VIEW rpr_serial_v6;
 -- {n} quantifier (exactly n)
 CREATE VIEW rpr_serial_v7 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -2163,7 +2157,6 @@ SELECT pg_get_viewdef('rpr_serial_v7'::regclass);
    a AS (val > 0) );
 (1 row)
 
-DROP VIEW rpr_serial_v7;
 -- Nested ALT pattern (tests deparse of complex nested structure)
 CREATE VIEW rpr_serial_v8 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -2202,7 +2195,6 @@ SELECT pg_get_viewdef('rpr_serial_v8'::regclass);
    d AS (val > 30) );
 (1 row)
 
-DROP VIEW rpr_serial_v8;
 -- Reluctant {1}? quantifier deparse through ruleutils
 CREATE VIEW rpr_quant_reluctant_v AS
 SELECT id, val, count(*) OVER w
@@ -2228,7 +2220,6 @@ SELECT pg_get_viewdef('rpr_quant_reluctant_v'::regclass);
    b AS (val > 0) );
 (1 row)
 
-DROP VIEW rpr_quant_reluctant_v;
 -- Materialized view (if supported)
 CREATE TABLE rpr_mview (id INT, val INT);
 INSERT INTO rpr_mview VALUES (1, 10), (2, 20), (3, 30);
@@ -2274,8 +2265,6 @@ SELECT * FROM rpr_mview_v1 ORDER BY id;
   3 |  30 |   0
 (3 rows)
 
-DROP MATERIALIZED VIEW rpr_mview_v1;
-DROP TABLE rpr_mview;
 -- CREATE TABLE AS SELECT with RPR
 CREATE TABLE rpr_ctas (id INT, val INT);
 INSERT INTO rpr_ctas VALUES (1, 10), (2, 20), (3, 15), (4, 25);
@@ -2677,8 +2666,6 @@ SELECT pg_get_viewdef('rpr_multiwin_v'::regclass);
    b AS (val <= 15) );
 (1 row)
 
-DROP VIEW rpr_multiwin_v;
-DROP TABLE rpr_multiwin;
 -- {n} quantifier display in view
 CREATE VIEW rpr_quant_n_v AS
 SELECT id, val, count(*) OVER w
@@ -2703,7 +2690,6 @@ SELECT pg_get_viewdef('rpr_quant_n_v'::regclass);
    a AS (val > 0) );
 (1 row)
 
-DROP VIEW rpr_quant_n_v;
 -- {n,} quantifier display in view
 CREATE VIEW rpr_quant_n_plus_v AS
 SELECT id, val, count(*) OVER w
@@ -2728,8 +2714,6 @@ SELECT pg_get_viewdef('rpr_quant_n_plus_v'::regclass);
    a AS (val > 0) );
 (1 row)
 
-DROP VIEW rpr_quant_n_plus_v;
-DROP TABLE rpr_serial;
 -- ============================================================
 -- Error Cases Tests
 -- ============================================================
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index 817269021f4..3c70a12874a 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -3,6 +3,9 @@
 -- Tests for Row Pattern Recognition EXPLAIN output
 -- ============================================================
 --
+-- Views and tables in this file are intentionally not dropped,
+-- so that pg_upgrade/pg_dump can test RPR syntax serialization.
+--
 -- This test suite validates EXPLAIN output for RPR queries,
 -- including NFA statistics shown in EXPLAIN ANALYZE:
 --   - NFA States: peak, total, merged
@@ -74,13 +77,13 @@ begin
 end;
 $$;
 -- Setup: Create test tables
-CREATE TEMP TABLE nfa_test (
+CREATE TABLE rpr_nfa_test (
     id serial,
     v int,
     cat char(1)
 );
 -- Insert test data: 100 rows with predictable pattern
-INSERT INTO nfa_test (v, cat)
+INSERT INTO rpr_nfa_test (v, cat)
 SELECT i,
        CASE
            WHEN i % 5 = 1 THEN 'A'
@@ -91,12 +94,12 @@ SELECT i,
        END
 FROM generate_series(1, 100) i;
 -- Additional test table with more complex patterns
-CREATE TEMP TABLE nfa_complex (
+CREATE TABLE rpr_nfa_complex (
     id serial,
     price int,
     trend char(1)  -- U=up, D=down, S=stable
 );
-INSERT INTO nfa_complex (price, trend)
+INSERT INTO rpr_nfa_complex (price, trend)
 VALUES
     (100, 'S'), (105, 'U'), (110, 'U'), (108, 'D'), (112, 'U'),
     (115, 'U'), (113, 'D'), (111, 'D'), (109, 'D'), (110, 'U'),
@@ -108,16 +111,16 @@ VALUES
 -- Basic NFA Statistics Tests
 -- ============================================================
 -- Simple pattern - should show basic statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev01 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (A B)
     DEFINE A AS cat = 'A', B AS cat = 'B'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev01'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line       
 ------------------
    PATTERN (a b) 
@@ -126,7 +129,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -143,21 +146,20 @@ WINDOW w AS (
    NFA Contexts: 2 peak, 101 total, 60 pruned
    NFA: 20 matched (len 2/2/2.0), 0 mismatched
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Pattern with no matches - 0 matched
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev02 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (X Y Z)
     DEFINE X AS cat = 'X', Y AS cat = 'Y', Z AS cat = 'Z'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev02'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (x y z) 
@@ -166,7 +168,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -182,21 +184,20 @@ WINDOW w AS (
    NFA States: 1 peak, 101 total, 0 merged
    NFA Contexts: 2 peak, 101 total, 100 pruned
    NFA: 0 matched, 0 mismatched
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Pattern matching every row - high match count
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev03 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (R)
     DEFINE R AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev03'), E'\n')) AS line WHERE line ~ 'PATTERN';
       line      
 ----------------
    PATTERN (r) 
@@ -205,7 +206,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -221,13 +222,12 @@ WINDOW w AS (
    NFA States: 2 peak, 101 total, 0 merged
    NFA Contexts: 2 peak, 101 total, 0 pruned
    NFA: 100 matched (len 1/1/1.0), 0 mismatched
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Regression test: Space before parenthesis in pattern deparse
 -- Verifies that "A (B | C)" correctly outputs as "a (b | c)" with space
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev04 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -235,7 +235,7 @@ WINDOW w AS (
     PATTERN (A (B | C))
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev04'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line          
 ------------------------
    PATTERN (a (b | c)) 
@@ -263,11 +263,10 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=20.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Regression test: Sequential alternations at same depth
 -- Verifies that "((B | C) (D | E))" correctly outputs as "(b | c) (d | e)"
 -- Previously failed due to missing parentheses on ALT depth decrease
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev05 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -275,7 +274,7 @@ WINDOW w AS (
     PATTERN (A ((B | C) (D | E))*)
     DEFINE A AS v % 5 = 1, B AS v % 5 = 2, C AS v % 5 = 3, D AS v % 5 = 4, E AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev05'), E'\n')) AS line WHERE line ~ 'PATTERN';
                line                
 -----------------------------------
    PATTERN (a ((b | c) (d | e))*) 
@@ -302,12 +301,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- State Statistics Tests (peak, total, merged)
 -- ============================================================
 -- Simple quantifier pattern - A+ with short matches (no merging)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev06 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -316,7 +314,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev06'), E'\n')) AS line WHERE line ~ 'PATTERN';
       line       
 -----------------
    PATTERN (a+) 
@@ -344,11 +342,10 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Alternation pattern - multiple state branches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev07 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -357,7 +354,7 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev07'), E'\n')) AS line WHERE line ~ 'PATTERN';
                line               
 ----------------------------------
    PATTERN ((a | b | c) (d | e)) 
@@ -366,7 +363,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -385,12 +382,11 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 101 total, 20 pruned
    NFA: 20 matched (len 2/2/2.0), 40 mismatched (len 2/2/2.0)
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Complex pattern with high state count
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev08 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -402,7 +398,7 @@ WINDOW w AS (
         B AS v % 3 = 2,
         C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev08'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN (a+ b* c+) 
@@ -434,9 +430,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Grouped pattern with quantifier - state count with grouping
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev09 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -445,7 +440,7 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev09'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN ((a b)+) 
@@ -474,10 +469,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- State explosion pattern - many alternations
 -- Pattern (A|B)(A|B)(A|B)(A|B) can create many parallel states
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev10 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -486,7 +480,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev10'), E'\n')) AS line WHERE line ~ 'PATTERN';
                                      line                                     
 ------------------------------------------------------------------------------
    PATTERN ((a | b) (a | b) (a | b) (a | b) (a | b) (a | b) (a | b) (a | b)) 
@@ -515,10 +509,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Consecutive ALT merge followed by different ALT
 -- Tests mergeConsecutiveAlts flush on ALT change: (A|B){2} (C|D)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev11 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -527,7 +520,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev11'), E'\n')) AS line WHERE line ~ 'PATTERN';
                  line                 
 --------------------------------------
    PATTERN ((a | b) (a | b) (c | d)) 
@@ -556,10 +549,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Consecutive ALT merge followed by non-ALT element
 -- Tests mergeConsecutiveAlts flush on non-ALT: (A|B){2} c
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev12 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -568,7 +560,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev12'), E'\n')) AS line WHERE line ~ 'PATTERN';
               line              
 --------------------------------
    PATTERN ((a | b) (a | b) c) 
@@ -597,9 +589,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ALT prefix/suffix absorbed into GROUP: (A|B) (A|B)+ (A|B) -> (A|B){3,}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev13 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -608,7 +599,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B)+ (A | B))
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev13'), E'\n')) AS line WHERE line ~ 'PATTERN';
                  line                  
 ---------------------------------------
    PATTERN ((a | b) (a | b)+ (a | b)) 
@@ -637,9 +628,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- High state count - alternation with plus quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev14 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -648,7 +638,7 @@ WINDOW w AS (
     PATTERN ((A | B | C)+ D)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3, D AS v % 4 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev14'), E'\n')) AS line WHERE line ~ 'PATTERN';
             line             
 -----------------------------
    PATTERN ((a | b | c)+ d) 
@@ -677,10 +667,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Early termination: first ALT branch (A) reaches FIN immediately,
 -- pruning second branch (A B+) before it can accumulate B repetitions.
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev15 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -689,7 +678,7 @@ WINDOW w AS (
     PATTERN ((A | A B)+)
     DEFINE A AS v = 1, B AS v > 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev15'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line           
 -------------------------
    PATTERN ((a | a b)+) 
@@ -717,9 +706,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Nested quantifiers causing state growth
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev16 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -728,7 +716,7 @@ WINDOW w AS (
     PATTERN (((A | B)+)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev16'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN (((a | b)+)+) 
@@ -757,12 +745,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=1000.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Context Statistics Tests (peak, total, pruned + absorbed/skipped)
 -- ============================================================
 -- Context absorption with unbounded quantifier at start
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev17 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -771,7 +758,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev17'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -800,9 +787,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- No absorption - bounded quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev18 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -811,7 +797,7 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev18'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN (a{2,4} b) 
@@ -840,9 +826,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Contexts skipped by SKIP PAST LAST ROW
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev19 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -851,7 +836,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 10 = 1, B AS v % 10 = 2, C AS v % 10 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev19'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (a b c) 
@@ -880,9 +865,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- High context absorption - unbounded group
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev20 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -891,7 +875,7 @@ WINDOW w AS (
     PATTERN ((A B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev20'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN ((a b)+ c) 
@@ -920,14 +904,13 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Match Length Statistics Tests
 -- ============================================================
 -- Fixed length matches - all same length
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev21 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -936,7 +919,7 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev21'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line          
 ------------------------
    PATTERN (a b c d e) 
@@ -945,7 +928,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -964,12 +947,11 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 101 total, 60 pruned
    NFA: 20 matched (len 5/5/5.0), 0 mismatched
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Variable length matches - min/max/avg differ
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev22 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -978,7 +960,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev22'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1007,9 +989,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Very long matches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev23 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 200) AS s(v)
 WINDOW w AS (
@@ -1018,7 +999,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v <= 195, B AS v > 195
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev23'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1047,9 +1028,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=200.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Uniform match length with mismatches from gap rows (v%20 = 11..15)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev24 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1060,7 +1040,7 @@ WINDOW w AS (
         A AS (v % 20 <> 0) AND (v % 20 <= 10 OR v % 20 > 15),
         B AS v % 20 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev24'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1091,13 +1071,12 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Mismatch Length Statistics Tests
 -- ============================================================
 -- Pattern with complete match every cycle: 0 mismatched
 -- A(1,2,3) B(4,5) C(6) repeats perfectly; X rows are pruned, not mismatched
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev25 AS
 SELECT count(*) OVER w
 FROM (
     SELECT v,
@@ -1113,7 +1092,7 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev25'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line         
 ----------------------
    PATTERN (a+ b+ c) 
@@ -1149,9 +1128,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Long partial matches that fail
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev26 AS
 SELECT count(*) OVER w
 FROM (
     SELECT i AS v,
@@ -1172,7 +1150,7 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev26'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line         
 ----------------------
    PATTERN (a+ b+ c) 
@@ -1213,12 +1191,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series i (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- JSON Format Tests
 -- ============================================================
 -- JSON format output with all statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev27 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1227,7 +1204,7 @@ WINDOW w AS (
     PATTERN (A+ B+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev27'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (a+ b+) 
@@ -1294,9 +1271,8 @@ WINDOW w AS (
  ]
 (1 row)
 
-DROP VIEW rpr_v;
 -- JSON format with match length statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev28 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1305,7 +1281,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev28'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1375,10 +1351,9 @@ WINDOW w AS (
  ]
 (1 row)
 
-DROP VIEW rpr_v;
 -- JSON format with mismatch statistics
 -- Pattern A B C expects 1,2,3 but gets 1,2,4 twice causing mismatches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev29 AS
 SELECT count(*) OVER w
 FROM (VALUES (1),(2),(4), (1),(2),(4), (1),(2),(3)) AS t(v)
 WINDOW w AS (
@@ -1387,7 +1362,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v = 1, B AS v = 2, C AS v = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev29'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (a b c) 
@@ -1456,10 +1431,9 @@ WINDOW w AS (
  ]
 (1 row)
 
-DROP VIEW rpr_v;
 -- JSON format with skipped context statistics
 -- Alternation pattern with SKIP PAST LAST ROW causes many contexts to be skipped
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev30 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1468,7 +1442,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev30'), E'\n')) AS line WHERE line ~ 'PATTERN';
                                      line                                     
 ------------------------------------------------------------------------------
    PATTERN ((a | b) (a | b) (a | b) (a | b) (a | b) (a | b) (a | b) (a | b)) 
@@ -1538,12 +1512,11 @@ WINDOW w AS (
  ]
 (1 row)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- XML Format Tests
 -- ============================================================
 -- XML format output
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev31 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -1552,7 +1525,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev31'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line       
 ------------------
    PATTERN (a b) 
@@ -1619,12 +1592,11 @@ WINDOW w AS (
  </explain>
 (1 row)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Multiple Partitions Tests
 -- ============================================================
 -- Statistics across multiple partitions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev32 AS
 SELECT count(*) OVER w
 FROM (
     SELECT p, v
@@ -1638,7 +1610,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev32'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1677,9 +1649,8 @@ WINDOW w AS (
                ->  Function Scan on generate_series v (actual rows=30.00 loops=3)
 (14 rows)
 
-DROP VIEW rpr_v;
 -- Different pattern behavior per partition
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev33 AS
 SELECT count(*) OVER w
 FROM (
     SELECT
@@ -1694,7 +1665,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS val < 5, B AS val >= 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev33'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -1732,12 +1703,11 @@ WINDOW w AS (
          ->  Function Scan on generate_series v (actual rows=50.00 loops=1)
 (12 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Edge Cases
 -- ============================================================
 -- Empty result set
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev34 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 0) AS s(v)
 WINDOW w AS (
@@ -1746,7 +1716,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v = 1, B AS v = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev34'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line       
 ------------------
    PATTERN (a b) 
@@ -1770,9 +1740,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=0.00 loops=1)
 (4 rows)
 
-DROP VIEW rpr_v;
 -- Single row
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev35 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1) AS s(v)
 WINDOW w AS (
@@ -1781,7 +1750,7 @@ WINDOW w AS (
     PATTERN (A)
     DEFINE A AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev35'), E'\n')) AS line WHERE line ~ 'PATTERN';
       line      
 ----------------
    PATTERN (a) 
@@ -1809,9 +1778,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=1.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Pattern longer than data
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev36 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 5) AS s(v)
 WINDOW w AS (
@@ -1822,7 +1790,7 @@ WINDOW w AS (
         A AS v = 1, B AS v = 2, C AS v = 3, D AS v = 4, E AS v = 5,
         F AS v = 6, G AS v = 7, H AS v = 8, I AS v = 9, J AS v = 10
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev36'), E'\n')) AS line WHERE line ~ 'PATTERN';
                line               
 ----------------------------------
    PATTERN (a b c d e f g h i j) 
@@ -1852,9 +1820,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=5.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- All rows match as single match
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev37 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1863,7 +1830,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev37'), E'\n')) AS line WHERE line ~ 'PATTERN';
       line       
 -----------------
    PATTERN (a+) 
@@ -1892,12 +1859,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Complex Pattern Tests
 -- ============================================================
 -- Nested groups
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev38 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1906,7 +1872,7 @@ WINDOW w AS (
     PATTERN (((A B) C)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev38'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line           
 -------------------------
    PATTERN (((a b) c)+) 
@@ -1935,11 +1901,10 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Multiple alternations
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev39 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1948,7 +1913,7 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev39'), E'\n')) AS line WHERE line ~ 'PATTERN';
                line               
 ----------------------------------
    PATTERN ((a | b) (c | d | e)) 
@@ -1957,7 +1922,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1976,12 +1941,11 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 101 total, 40 pruned
    NFA: 20 matched (len 2/2/2.0), 20 mismatched (len 2/2/2.0)
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Optional elements
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev40 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1990,7 +1954,7 @@ WINDOW w AS (
     PATTERN (A B? C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev40'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a b? c) 
@@ -2019,9 +1983,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Bounded quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev41 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -2030,7 +1993,7 @@ WINDOW w AS (
     PATTERN (A{2,5} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev41'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN (a{2,5} b) 
@@ -2059,9 +2022,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Star quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev42 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2070,7 +2032,7 @@ WINDOW w AS (
     PATTERN (A B* C)
     DEFINE A AS v % 10 = 1, B AS v % 10 IN (2,3,4,5,6,7,8), C AS v % 10 = 9
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev42'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a b* c) 
@@ -2099,21 +2061,20 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Real-world Pattern Examples
 -- ============================================================
 -- Stock price pattern - V-shape (down then up)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev43 AS
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (D+ U+)
     DEFINE D AS trend = 'D', U AS trend = 'U'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev43'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (d+ u+) 
@@ -2122,7 +2083,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -2139,21 +2100,20 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 31 total, 3 pruned
    NFA: 3 matched (len 3/14/8.0), 1 mismatched (len 3/3/3.0)
    NFA: 9 absorbed (len 1/1/1.0), 14 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_complex (actual rows=30.00 loops=1)
+   ->  Seq Scan on rpr_nfa_complex (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Stock price pattern - peak (up, stable, down)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev44 AS
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (U+ S* D+)
     DEFINE U AS trend = 'U', S AS trend = 'S', D AS trend = 'D'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev44'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN (u+ s* d+) 
@@ -2162,7 +2122,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -2179,12 +2139,11 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 31 total, 1 pruned
    NFA: 4 matched (len 3/11/7.2), 0 mismatched
    NFA: 12 absorbed (len 1/1/1.0), 13 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_complex (actual rows=30.00 loops=1)
+   ->  Seq Scan on rpr_nfa_complex (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Consecutive increasing values (using PREV)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev45 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2193,7 +2152,7 @@ WINDOW w AS (
     PATTERN (A{3,})
     DEFINE A AS v > PREV(v) OR PREV(v) IS NULL
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev45'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (a{3,}) 
@@ -2222,12 +2181,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Performance-oriented Tests
 -- ============================================================
 -- Large dataset with simple pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev46 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -2236,7 +2194,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev46'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line       
 ------------------
    PATTERN (a b) 
@@ -2265,9 +2223,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=1000.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Large dataset with absorption
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev47 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -2276,7 +2233,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 100 <> 0, B AS v % 100 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev47'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -2305,9 +2262,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=1000.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- High state merge ratio
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev48 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -2316,7 +2272,7 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev48'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line           
 -------------------------
    PATTERN ((a | b)+ c) 
@@ -2345,12 +2301,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- INITIAL vs no INITIAL comparison
 -- ============================================================
 -- With INITIAL keyword
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev49 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2360,7 +2315,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev49'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -2390,9 +2345,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Without INITIAL keyword (same behavior currently)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev50 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2401,7 +2355,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev50'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -2430,12 +2384,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Quantifier Variations
 -- ============================================================
 -- Plus quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev51 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -2444,7 +2397,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 4 <> 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev51'), E'\n')) AS line WHERE line ~ 'PATTERN';
       line       
 -----------------
    PATTERN (a+) 
@@ -2473,9 +2426,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Star quantifier (zero or more)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev52 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -2484,7 +2436,7 @@ WINDOW w AS (
     PATTERN (A* B)
     DEFINE A AS v % 4 IN (1, 2), B AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev52'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a* b) 
@@ -2513,9 +2465,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Question mark (zero or one)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev53 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -2524,7 +2475,7 @@ WINDOW w AS (
     PATTERN (A? B C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev53'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a? b c) 
@@ -2553,9 +2504,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Exact count {n}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev54 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2564,7 +2514,7 @@ WINDOW w AS (
     PATTERN (A{3} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev54'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a{3} b) 
@@ -2593,9 +2543,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Range {n,m}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev55 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2604,7 +2553,7 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev55'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line          
 -----------------------
    PATTERN (a{2,4} b) 
@@ -2633,9 +2582,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- At least {n,}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev56 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2644,7 +2592,7 @@ WINDOW w AS (
     PATTERN (A{3,} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev56'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line         
 ----------------------
    PATTERN (a{3,} b) 
@@ -2673,13 +2621,12 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Regression Tests for Statistics Accuracy
 -- ============================================================
 -- Verify state count accuracy
 -- Pattern A+ B with 20 rows should show predictable state behavior
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev57 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -2688,7 +2635,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev57'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -2717,9 +2664,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=20.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Verify context count with known absorption
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev58 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2728,7 +2674,7 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 IN (1,2,3,4,5,6,7), B AS v % 10 = 8, C AS v % 10 = 9
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev58'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a+ b c) 
@@ -2757,9 +2703,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Verify match length with fixed-length pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev59 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2768,7 +2713,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev59'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line        
 --------------------
    PATTERN (a b c) 
@@ -2797,21 +2742,20 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Alternation Pattern Tests
 -- ============================================================
 -- Simple alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev60 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN ((A | B) C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev60'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line          
 ------------------------
    PATTERN ((a | b) c) 
@@ -2820,7 +2764,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -2837,14 +2781,13 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 101 total, 40 pruned
    NFA: 20 matched (len 2/2/2.0), 20 mismatched (len 2/2/2.0)
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Multiple items in alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev61 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -2853,7 +2796,7 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev61'), E'\n')) AS line WHERE line ~ 'PATTERN';
               line              
 --------------------------------
    PATTERN ((a | b | c | d) e) 
@@ -2862,7 +2805,7 @@ SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -2881,12 +2824,11 @@ WINDOW w AS (
    NFA Contexts: 3 peak, 101 total, 0 pruned
    NFA: 20 matched (len 2/2/2.0), 60 mismatched (len 2/2/2.0)
    NFA: 0 absorbed, 20 skipped (len 1/1/1.0)
-   ->  Seq Scan on nfa_test (actual rows=100.00 loops=1)
+   ->  Seq Scan on rpr_nfa_test (actual rows=100.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Alternation with quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev62 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2895,7 +2837,7 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev62'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line           
 -------------------------
    PATTERN ((a | b)+ c) 
@@ -2924,9 +2866,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Multiple alternatives (4+)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev63 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -2934,7 +2875,7 @@ WINDOW w AS (
     PATTERN (A | B | C | D | E)
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev63'), E'\n')) AS line WHERE line ~ 'PATTERN';
               line              
 --------------------------------
    PATTERN (a | b | c | d | e) 
@@ -2961,9 +2902,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Alternation at start
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev64 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -2971,7 +2911,7 @@ WINDOW w AS (
     PATTERN ((A | B) C D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev64'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN ((a | b) c d) 
@@ -2999,9 +2939,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Multiple sequential alternations
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev65 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -3009,7 +2948,7 @@ WINDOW w AS (
     PATTERN ((A | B) C (D | E) F)
     DEFINE A AS v % 6 = 0, B AS v % 6 = 1, C AS v % 6 = 2, D AS v % 6 = 3, E AS v % 6 = 4, F AS v % 6 = 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev65'), E'\n')) AS line WHERE line ~ 'PATTERN';
                line               
 ----------------------------------
    PATTERN ((a | b) c (d | e) f) 
@@ -3036,9 +2975,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=100.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Quantified alternatives
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev66 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3046,7 +2984,7 @@ WINDOW w AS (
     PATTERN ((A+ | B+) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev66'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN ((a+ | b+) c) 
@@ -3074,9 +3012,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Alternation at end
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev67 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3084,7 +3021,7 @@ WINDOW w AS (
     PATTERN (A B (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev67'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN (a b (c | d)) 
@@ -3112,10 +3049,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Nested ALT at start of branch inside outer ALT
 -- Pattern: (A ((B | C) D | E)) - preceding VAR + inner ALT as first branch element
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev68 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -3123,7 +3059,7 @@ WINDOW w AS (
     PATTERN (A ((B | C) D | E))
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev68'), E'\n')) AS line WHERE line ~ 'PATTERN';
               line              
 --------------------------------
    PATTERN (a ((b | c) d | e)) 
@@ -3150,10 +3086,9 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=20.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Nested ALT at end of branch inside outer ALT
 -- Pattern: (C (A | B) | D) - inner ALT is last element in outer branch
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev69 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -3161,7 +3096,7 @@ WINDOW w AS (
     PATTERN (C (A | B) | D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev69'), E'\n')) AS line WHERE line ~ 'PATTERN';
             line            
 ----------------------------
    PATTERN (c (a | b) | d) 
@@ -3188,12 +3123,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=20.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Group Pattern Tests
 -- ============================================================
 -- Simple group
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev70 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -3202,7 +3136,7 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev70'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN ((a b)+) 
@@ -3231,9 +3165,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Group with bounded quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev71 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -3242,7 +3175,7 @@ WINDOW w AS (
     PATTERN ((A B){2,4})
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev71'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line           
 -------------------------
    PATTERN ((a b){2,4}) 
@@ -3271,9 +3204,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Nested groups
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev72 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3282,7 +3214,7 @@ WINDOW w AS (
     PATTERN (((A B){2})+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev72'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN (((a b){2})+) 
@@ -3311,9 +3243,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Deep nesting (3+ levels)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev73 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -3321,7 +3252,7 @@ WINDOW w AS (
     PATTERN ((((A | B)+)+)+)
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev73'), E'\n')) AS line WHERE line ~ 'PATTERN';
             line             
 -----------------------------
    PATTERN ((((a | b)+)+)+) 
@@ -3349,9 +3280,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=40.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Bounded quantifier on alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev74 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3359,7 +3289,7 @@ WINDOW w AS (
     PATTERN ((A | B){2,3} C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev74'), E'\n')) AS line WHERE line ~ 'PATTERN';
             line             
 -----------------------------
    PATTERN ((a | b){2,3} c) 
@@ -3387,9 +3317,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Nested groups with quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev75 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3397,7 +3326,7 @@ WINDOW w AS (
     PATTERN (((A B)+ C)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev75'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN (((a b)+ c)*) 
@@ -3425,9 +3354,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Partial nested quantification
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev76 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -3435,7 +3363,7 @@ WINDOW w AS (
     PATTERN ((A (B C)+)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev76'), E'\n')) AS line WHERE line ~ 'PATTERN';
            line           
 --------------------------
    PATTERN ((a (b c)+)*) 
@@ -3463,12 +3391,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=60.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Window Function Combinations
 -- ============================================================
 -- count(*) with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev77 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -3477,7 +3404,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev77'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3506,9 +3433,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- first_value with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev78 AS
 SELECT first_value(v) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -3517,7 +3443,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev78'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3546,9 +3472,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- last_value with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev79 AS
 SELECT last_value(v) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -3557,7 +3482,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev79'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3586,9 +3511,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Multiple window functions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev80 AS
 SELECT
     count(*) OVER w,
     first_value(v) OVER w,
@@ -3600,7 +3524,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev80'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3632,12 +3556,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- DEFINE Expression Variations
 -- ============================================================
 -- Complex boolean expressions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev81 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -3648,7 +3571,7 @@ WINDOW w AS (
         A AS (v % 5 <> 0) AND (v % 3 <> 0),
         B AS (v % 5 = 0) OR (v % 3 = 0)
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev81'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3679,9 +3602,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=50.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- Using PREV function
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev82 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -3693,7 +3615,7 @@ WINDOW w AS (
         U AS v > PREV(v),
         D AS v < PREV(v)
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev82'), E'\n')) AS line WHERE line ~ 'PATTERN';
          line         
 ----------------------
    PATTERN (s u+ d+) 
@@ -3724,9 +3646,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=30.00 loops=1)
 (8 rows)
 
-DROP VIEW rpr_v;
 -- Using NULL comparisons
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev83 AS
 SELECT count(*) OVER w
 FROM (
     SELECT CASE WHEN v % 5 = 0 THEN NULL ELSE v END AS v
@@ -3738,7 +3659,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v IS NOT NULL, B AS v IS NULL
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev83'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line        
 -------------------
    PATTERN (a+ b) 
@@ -3770,12 +3691,11 @@ WINDOW w AS (
    ->  Function Scan on generate_series v (actual rows=30.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- ============================================================
 -- Large Scale Statistics Verification
 -- ============================================================
 -- 500 rows - verify statistics scale correctly
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev84 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -3784,7 +3704,7 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 < 7, B AS v % 10 = 7, C AS v % 10 = 8
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev84'), E'\n')) AS line WHERE line ~ 'PATTERN';
         line         
 ---------------------
    PATTERN (a+ b c) 
@@ -3813,9 +3733,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- High match count scenario
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev85 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -3824,7 +3743,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev85'), E'\n')) AS line WHERE line ~ 'PATTERN';
        line       
 ------------------
    PATTERN (a b) 
@@ -3853,9 +3772,8 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
 -- High skip count scenario
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev86 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -3869,7 +3787,7 @@ WINDOW w AS (
         D AS v % 100 = 4,
         E AS v % 100 = 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev86'), E'\n')) AS line WHERE line ~ 'PATTERN';
           line          
 ------------------------
    PATTERN (a b c d e) 
@@ -3903,7 +3821,3 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
-DROP VIEW rpr_v;
--- Cleanup
-DROP TABLE nfa_test;
-DROP TABLE nfa_complex;
diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql
index da28de1a8f9..453860c1499 100644
--- a/src/test/regress/sql/rpr_base.sql
+++ b/src/test/regress/sql/rpr_base.sql
@@ -12,7 +12,7 @@
 --   Quantifiers Tests
 --   Navigation Functions Tests
 --   SKIP TO / INITIAL Tests
---   Serialization/Deserialization Tests
+--   Serialization/Deserialization Tests (objects kept for pg_upgrade/pg_dump)
 --   Error Cases Tests
 --
 -- Planner Layer:
@@ -1408,8 +1408,6 @@ SELECT * FROM rpr_serial_v1 ORDER BY id;
 -- Verify deparsing
 SELECT pg_get_viewdef('rpr_serial_v1'::regclass);
 
-DROP VIEW rpr_serial_v1;
-
 -- Complex pattern with alternation
 CREATE VIEW rpr_serial_v2 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1424,8 +1422,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v2 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v2'::regclass);
 
-DROP VIEW rpr_serial_v2;
-
 -- Pattern with grouping and quantifiers
 CREATE VIEW rpr_serial_v3 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1443,8 +1439,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v3 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v3'::regclass);
 
-DROP VIEW rpr_serial_v3;
-
 -- All features combined
 CREATE VIEW rpr_serial_v4 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1465,8 +1459,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v4 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v4'::regclass);
 
-DROP VIEW rpr_serial_v4;
-
 -- Additional quantifiers for deparsing coverage
 
 -- ? quantifier (zero or one)
@@ -1483,8 +1475,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v5 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v5'::regclass);
 
-DROP VIEW rpr_serial_v5;
-
 -- {n,} quantifier (n or more)
 CREATE VIEW rpr_serial_v6 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1499,8 +1489,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v6 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v6'::regclass);
 
-DROP VIEW rpr_serial_v6;
-
 -- {n} quantifier (exactly n)
 CREATE VIEW rpr_serial_v7 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1515,8 +1503,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v7 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v7'::regclass);
 
-DROP VIEW rpr_serial_v7;
-
 -- Nested ALT pattern (tests deparse of complex nested structure)
 CREATE VIEW rpr_serial_v8 AS
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -1531,8 +1517,6 @@ WINDOW w AS (
 SELECT * FROM rpr_serial_v8 ORDER BY id;
 SELECT pg_get_viewdef('rpr_serial_v8'::regclass);
 
-DROP VIEW rpr_serial_v8;
-
 -- Reluctant {1}? quantifier deparse through ruleutils
 CREATE VIEW rpr_quant_reluctant_v AS
 SELECT id, val, count(*) OVER w
@@ -1543,7 +1527,6 @@ WINDOW w AS (ORDER BY id
              PATTERN (A{1}? B)
              DEFINE A AS val > 0, B AS val > 0);
 SELECT pg_get_viewdef('rpr_quant_reluctant_v'::regclass);
-DROP VIEW rpr_quant_reluctant_v;
 
 -- Materialized view (if supported)
 
@@ -1567,9 +1550,6 @@ SELECT pg_get_viewdef('rpr_mview_v1'::regclass);
 REFRESH MATERIALIZED VIEW rpr_mview_v1;
 SELECT * FROM rpr_mview_v1 ORDER BY id;
 
-DROP MATERIALIZED VIEW rpr_mview_v1;
-DROP TABLE rpr_mview;
-
 -- CREATE TABLE AS SELECT with RPR
 CREATE TABLE rpr_ctas (id INT, val INT);
 INSERT INTO rpr_ctas VALUES (1, 10), (2, 20), (3, 15), (4, 25);
@@ -1863,9 +1843,6 @@ WINDOW
 SELECT * FROM rpr_multiwin_v ORDER BY id;
 SELECT pg_get_viewdef('rpr_multiwin_v'::regclass);
 
-DROP VIEW rpr_multiwin_v;
-DROP TABLE rpr_multiwin;
-
 -- {n} quantifier display in view
 CREATE VIEW rpr_quant_n_v AS
 SELECT id, val, count(*) OVER w
@@ -1876,7 +1853,6 @@ WINDOW w AS (ORDER BY id
              PATTERN (A{3})
              DEFINE A AS val > 0);
 SELECT pg_get_viewdef('rpr_quant_n_v'::regclass);
-DROP VIEW rpr_quant_n_v;
 
 -- {n,} quantifier display in view
 CREATE VIEW rpr_quant_n_plus_v AS
@@ -1888,9 +1864,6 @@ WINDOW w AS (ORDER BY id
              PATTERN (A{2,})
              DEFINE A AS val > 0);
 SELECT pg_get_viewdef('rpr_quant_n_plus_v'::regclass);
-DROP VIEW rpr_quant_n_plus_v;
-
-DROP TABLE rpr_serial;
 
 -- ============================================================
 -- Error Cases Tests
diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql
index 4bb49650bb7..8e22382a68e 100644
--- a/src/test/regress/sql/rpr_explain.sql
+++ b/src/test/regress/sql/rpr_explain.sql
@@ -3,6 +3,9 @@
 -- Tests for Row Pattern Recognition EXPLAIN output
 -- ============================================================
 --
+-- Views and tables in this file are intentionally not dropped,
+-- so that pg_upgrade/pg_dump can test RPR syntax serialization.
+--
 -- This test suite validates EXPLAIN output for RPR queries,
 -- including NFA statistics shown in EXPLAIN ANALYZE:
 --   - NFA States: peak, total, merged
@@ -76,14 +79,14 @@ end;
 $$;
 
 -- Setup: Create test tables
-CREATE TEMP TABLE nfa_test (
+CREATE TABLE rpr_nfa_test (
     id serial,
     v int,
     cat char(1)
 );
 
 -- Insert test data: 100 rows with predictable pattern
-INSERT INTO nfa_test (v, cat)
+INSERT INTO rpr_nfa_test (v, cat)
 SELECT i,
        CASE
            WHEN i % 5 = 1 THEN 'A'
@@ -95,13 +98,13 @@ SELECT i,
 FROM generate_series(1, 100) i;
 
 -- Additional test table with more complex patterns
-CREATE TEMP TABLE nfa_complex (
+CREATE TABLE rpr_nfa_complex (
     id serial,
     price int,
     trend char(1)  -- U=up, D=down, S=stable
 );
 
-INSERT INTO nfa_complex (price, trend)
+INSERT INTO rpr_nfa_complex (price, trend)
 VALUES
     (100, 'S'), (105, 'U'), (110, 'U'), (108, 'D'), (112, 'U'),
     (115, 'U'), (113, 'D'), (111, 'D'), (109, 'D'), (110, 'U'),
@@ -115,77 +118,74 @@ VALUES
 -- ============================================================
 
 -- Simple pattern - should show basic statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev01 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (A B)
     DEFINE A AS cat = 'A', B AS cat = 'B'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev01'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (A B)
     DEFINE A AS cat = ''A'', B AS cat = ''B''
 )');
-DROP VIEW rpr_v;
 
 -- Pattern with no matches - 0 matched
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev02 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (X Y Z)
     DEFINE X AS cat = 'X', Y AS cat = 'Y', Z AS cat = 'Z'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev02'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (X Y Z)
     DEFINE X AS cat = ''X'', Y AS cat = ''Y'', Z AS cat = ''Z''
 );');
-DROP VIEW rpr_v;
 
 -- Pattern matching every row - high match count
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev03 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (R)
     DEFINE R AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev03'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (R)
     DEFINE R AS TRUE
 );');
-DROP VIEW rpr_v;
 
 -- Regression test: Space before parenthesis in pattern deparse
 -- Verifies that "A (B | C)" correctly outputs as "a (b | c)" with space
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev04 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -193,7 +193,7 @@ WINDOW w AS (
     PATTERN (A (B | C))
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev04'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -203,12 +203,11 @@ WINDOW w AS (
     PATTERN (A (B | C))
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Regression test: Sequential alternations at same depth
 -- Verifies that "((B | C) (D | E))" correctly outputs as "(b | c) (d | e)"
 -- Previously failed due to missing parentheses on ALT depth decrease
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev05 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -216,7 +215,7 @@ WINDOW w AS (
     PATTERN (A ((B | C) (D | E))*)
     DEFINE A AS v % 5 = 1, B AS v % 5 = 2, C AS v % 5 = 3, D AS v % 5 = 4, E AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev05'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -226,14 +225,13 @@ WINDOW w AS (
     PATTERN (A ((B | C) (D | E))*)
     DEFINE A AS v % 5 = 1, B AS v % 5 = 2, C AS v % 5 = 3, D AS v % 5 = 4, E AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- State Statistics Tests (peak, total, merged)
 -- ============================================================
 
 -- Simple quantifier pattern - A+ with short matches (no merging)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev06 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -242,7 +240,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev06'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -253,12 +251,11 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 2 = 1
 );');
-DROP VIEW rpr_v;
 
 -- Alternation pattern - multiple state branches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev07 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -267,11 +264,11 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev07'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -280,10 +277,9 @@ WINDOW w AS (
         A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C'',
         D AS cat = ''D'', E AS cat = ''E''
 );');
-DROP VIEW rpr_v;
 
 -- Complex pattern with high state count
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev08 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -295,7 +291,7 @@ WINDOW w AS (
         B AS v % 3 = 2,
         C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev08'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -309,10 +305,9 @@ WINDOW w AS (
         B AS v % 3 = 2,
         C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Grouped pattern with quantifier - state count with grouping
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev09 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -321,7 +316,7 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev09'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -332,11 +327,10 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- State explosion pattern - many alternations
 -- Pattern (A|B)(A|B)(A|B)(A|B) can create many parallel states
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev10 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -345,7 +339,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev10'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -356,11 +350,10 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Consecutive ALT merge followed by different ALT
 -- Tests mergeConsecutiveAlts flush on ALT change: (A|B){2} (C|D)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev11 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -369,7 +362,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev11'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -380,11 +373,10 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Consecutive ALT merge followed by non-ALT element
 -- Tests mergeConsecutiveAlts flush on non-ALT: (A|B){2} c
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev12 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -393,7 +385,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev12'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -404,10 +396,9 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- ALT prefix/suffix absorbed into GROUP: (A|B) (A|B)+ (A|B) -> (A|B){3,}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev13 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -416,7 +407,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B)+ (A | B))
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev13'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -427,10 +418,9 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B)+ (A | B))
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );');
-DROP VIEW rpr_v;
 
 -- High state count - alternation with plus quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev14 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -439,7 +429,7 @@ WINDOW w AS (
     PATTERN ((A | B | C)+ D)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3, D AS v % 4 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev14'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -450,11 +440,10 @@ WINDOW w AS (
     PATTERN ((A | B | C)+ D)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3, D AS v % 4 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Early termination: first ALT branch (A) reaches FIN immediately,
 -- pruning second branch (A B+) before it can accumulate B repetitions.
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev15 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -463,7 +452,7 @@ WINDOW w AS (
     PATTERN ((A | A B)+)
     DEFINE A AS v = 1, B AS v > 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev15'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -474,10 +463,9 @@ WINDOW w AS (
     PATTERN ((A | A B)+)
     DEFINE A AS v = 1, B AS v > 1
 );');
-DROP VIEW rpr_v;
 
 -- Nested quantifiers causing state growth
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev16 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -486,7 +474,7 @@ WINDOW w AS (
     PATTERN (((A | B)+)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev16'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -497,14 +485,13 @@ WINDOW w AS (
     PATTERN (((A | B)+)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Context Statistics Tests (peak, total, pruned + absorbed/skipped)
 -- ============================================================
 
 -- Context absorption with unbounded quantifier at start
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev17 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -513,7 +500,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev17'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -524,10 +511,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- No absorption - bounded quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev18 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -536,7 +522,7 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev18'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -547,10 +533,9 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Contexts skipped by SKIP PAST LAST ROW
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev19 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -559,7 +544,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 10 = 1, B AS v % 10 = 2, C AS v % 10 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev19'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -570,10 +555,9 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 10 = 1, B AS v % 10 = 2, C AS v % 10 = 3
 );');
-DROP VIEW rpr_v;
 
 -- High context absorption - unbounded group
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev20 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -582,7 +566,7 @@ WINDOW w AS (
     PATTERN ((A B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev20'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -593,16 +577,15 @@ WINDOW w AS (
     PATTERN ((A B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Match Length Statistics Tests
 -- ============================================================
 
 -- Fixed length matches - all same length
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev21 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -611,11 +594,11 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev21'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -624,10 +607,9 @@ WINDOW w AS (
         A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C'',
         D AS cat = ''D'', E AS cat = ''E''
 );');
-DROP VIEW rpr_v;
 
 -- Variable length matches - min/max/avg differ
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev22 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -636,7 +618,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev22'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -647,10 +629,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Very long matches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev23 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 200) AS s(v)
 WINDOW w AS (
@@ -659,7 +640,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v <= 195, B AS v > 195
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev23'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -670,10 +651,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v <= 195, B AS v > 195
 );');
-DROP VIEW rpr_v;
 
 -- Uniform match length with mismatches from gap rows (v%20 = 11..15)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev24 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -684,7 +664,7 @@ WINDOW w AS (
         A AS (v % 20 <> 0) AND (v % 20 <= 10 OR v % 20 > 15),
         B AS v % 20 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev24'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -697,7 +677,6 @@ WINDOW w AS (
         A AS (v % 20 <> 0) AND (v % 20 <= 10 OR v % 20 > 15),
         B AS v % 20 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Mismatch Length Statistics Tests
@@ -705,7 +684,7 @@ DROP VIEW rpr_v;
 
 -- Pattern with complete match every cycle: 0 mismatched
 -- A(1,2,3) B(4,5) C(6) repeats perfectly; X rows are pruned, not mismatched
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev25 AS
 SELECT count(*) OVER w
 FROM (
     SELECT v,
@@ -721,7 +700,7 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev25'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -739,10 +718,9 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C''
 );');
-DROP VIEW rpr_v;
 
 -- Long partial matches that fail
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev26 AS
 SELECT count(*) OVER w
 FROM (
     SELECT i AS v,
@@ -763,7 +741,7 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev26'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -786,14 +764,13 @@ WINDOW w AS (
     PATTERN (A+ B+ C)
     DEFINE A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C''
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- JSON Format Tests
 -- ============================================================
 
 -- JSON format output with all statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev27 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -802,7 +779,7 @@ WINDOW w AS (
     PATTERN (A+ B+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev27'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
 SELECT count(*) OVER w
@@ -813,10 +790,9 @@ WINDOW w AS (
     PATTERN (A+ B+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2
 )');
-DROP VIEW rpr_v;
 
 -- JSON format with match length statistics
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev28 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -825,7 +801,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev28'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
 SELECT count(*) OVER w
@@ -836,11 +812,10 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 )');
-DROP VIEW rpr_v;
 
 -- JSON format with mismatch statistics
 -- Pattern A B C expects 1,2,3 but gets 1,2,4 twice causing mismatches
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev29 AS
 SELECT count(*) OVER w
 FROM (VALUES (1),(2),(4), (1),(2),(4), (1),(2),(3)) AS t(v)
 WINDOW w AS (
@@ -849,7 +824,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v = 1, B AS v = 2, C AS v = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev29'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
 SELECT count(*) OVER w
@@ -860,11 +835,10 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v = 1, B AS v = 2, C AS v = 3
 )');
-DROP VIEW rpr_v;
 
 -- JSON format with skipped context statistics
 -- Alternation pattern with SKIP PAST LAST ROW causes many contexts to be skipped
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev30 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -873,7 +847,7 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev30'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT JSON)
 SELECT count(*) OVER w
@@ -884,14 +858,13 @@ WINDOW w AS (
     PATTERN ((A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B) (A | B))
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 )');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- XML Format Tests
 -- ============================================================
 
 -- XML format output
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev31 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -900,7 +873,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev31'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF, FORMAT XML)
 SELECT count(*) OVER w
@@ -911,14 +884,13 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 )');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Multiple Partitions Tests
 -- ============================================================
 
 -- Statistics across multiple partitions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev32 AS
 SELECT count(*) OVER w
 FROM (
     SELECT p, v
@@ -932,7 +904,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev32'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -948,10 +920,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Different pattern behavior per partition
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev33 AS
 SELECT count(*) OVER w
 FROM (
     SELECT
@@ -966,7 +937,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS val < 5, B AS val >= 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev33'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -983,14 +954,13 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS val < 5, B AS val >= 5
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Edge Cases
 -- ============================================================
 
 -- Empty result set
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev34 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 0) AS s(v)
 WINDOW w AS (
@@ -999,7 +969,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v = 1, B AS v = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev34'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1010,10 +980,9 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v = 1, B AS v = 2
 );');
-DROP VIEW rpr_v;
 
 -- Single row
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev35 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1) AS s(v)
 WINDOW w AS (
@@ -1022,7 +991,7 @@ WINDOW w AS (
     PATTERN (A)
     DEFINE A AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev35'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1033,10 +1002,9 @@ WINDOW w AS (
     PATTERN (A)
     DEFINE A AS TRUE
 );');
-DROP VIEW rpr_v;
 
 -- Pattern longer than data
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev36 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 5) AS s(v)
 WINDOW w AS (
@@ -1047,7 +1015,7 @@ WINDOW w AS (
         A AS v = 1, B AS v = 2, C AS v = 3, D AS v = 4, E AS v = 5,
         F AS v = 6, G AS v = 7, H AS v = 8, I AS v = 9, J AS v = 10
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev36'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1060,10 +1028,9 @@ WINDOW w AS (
         A AS v = 1, B AS v = 2, C AS v = 3, D AS v = 4, E AS v = 5,
         F AS v = 6, G AS v = 7, H AS v = 8, I AS v = 9, J AS v = 10
 );');
-DROP VIEW rpr_v;
 
 -- All rows match as single match
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev37 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1072,7 +1039,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS TRUE
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev37'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1083,14 +1050,13 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS TRUE
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Complex Pattern Tests
 -- ============================================================
 
 -- Nested groups
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev38 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1099,7 +1065,7 @@ WINDOW w AS (
     PATTERN (((A B) C)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev38'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1110,12 +1076,11 @@ WINDOW w AS (
     PATTERN (((A B) C)+)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Multiple alternations
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev39 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1124,11 +1089,11 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev39'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1137,10 +1102,9 @@ WINDOW w AS (
         A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C'',
         D AS cat = ''D'', E AS cat = ''E''
 );');
-DROP VIEW rpr_v;
 
 -- Optional elements
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev40 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1149,7 +1113,7 @@ WINDOW w AS (
     PATTERN (A B? C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev40'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1160,10 +1124,9 @@ WINDOW w AS (
     PATTERN (A B? C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Bounded quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev41 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1172,7 +1135,7 @@ WINDOW w AS (
     PATTERN (A{2,5} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev41'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1183,10 +1146,9 @@ WINDOW w AS (
     PATTERN (A{2,5} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Star quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev42 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1195,7 +1157,7 @@ WINDOW w AS (
     PATTERN (A B* C)
     DEFINE A AS v % 10 = 1, B AS v % 10 IN (2,3,4,5,6,7,8), C AS v % 10 = 9
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev42'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1206,60 +1168,57 @@ WINDOW w AS (
     PATTERN (A B* C)
     DEFINE A AS v % 10 = 1, B AS v % 10 IN (2,3,4,5,6,7,8), C AS v % 10 = 9
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Real-world Pattern Examples
 -- ============================================================
 
 -- Stock price pattern - V-shape (down then up)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev43 AS
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (D+ U+)
     DEFINE D AS trend = 'D', U AS trend = 'U'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev43'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (D+ U+)
     DEFINE D AS trend = ''D'', U AS trend = ''U''
 );');
-DROP VIEW rpr_v;
 
 -- Stock price pattern - peak (up, stable, down)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev44 AS
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (U+ S* D+)
     DEFINE U AS trend = 'U', S AS trend = 'S', D AS trend = 'D'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev44'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_complex
+FROM rpr_nfa_complex
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN (U+ S* D+)
     DEFINE U AS trend = ''U'', S AS trend = ''S'', D AS trend = ''D''
 );');
-DROP VIEW rpr_v;
 
 -- Consecutive increasing values (using PREV)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev45 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1268,7 +1227,7 @@ WINDOW w AS (
     PATTERN (A{3,})
     DEFINE A AS v > PREV(v) OR PREV(v) IS NULL
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev45'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1279,14 +1238,13 @@ WINDOW w AS (
     PATTERN (A{3,})
     DEFINE A AS v > PREV(v) OR PREV(v) IS NULL
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Performance-oriented Tests
 -- ============================================================
 
 -- Large dataset with simple pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev46 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -1295,7 +1253,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev46'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1306,10 +1264,9 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Large dataset with absorption
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev47 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 1000) AS s(v)
 WINDOW w AS (
@@ -1318,7 +1275,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 100 <> 0, B AS v % 100 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev47'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1329,10 +1286,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 100 <> 0, B AS v % 100 = 0
 );');
-DROP VIEW rpr_v;
 
 -- High state merge ratio
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev48 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -1341,7 +1297,7 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev48'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1352,14 +1308,13 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- INITIAL vs no INITIAL comparison
 -- ============================================================
 
 -- With INITIAL keyword
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev49 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1369,7 +1324,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev49'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1381,10 +1336,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Without INITIAL keyword (same behavior currently)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev50 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1393,7 +1347,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev50'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1404,14 +1358,13 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Quantifier Variations
 -- ============================================================
 
 -- Plus quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev51 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1420,7 +1373,7 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 4 <> 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev51'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1431,10 +1384,9 @@ WINDOW w AS (
     PATTERN (A+)
     DEFINE A AS v % 4 <> 0
 );');
-DROP VIEW rpr_v;
 
 -- Star quantifier (zero or more)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev52 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1443,7 +1395,7 @@ WINDOW w AS (
     PATTERN (A* B)
     DEFINE A AS v % 4 IN (1, 2), B AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev52'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1454,10 +1406,9 @@ WINDOW w AS (
     PATTERN (A* B)
     DEFINE A AS v % 4 IN (1, 2), B AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Question mark (zero or one)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev53 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1466,7 +1417,7 @@ WINDOW w AS (
     PATTERN (A? B C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev53'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1477,10 +1428,9 @@ WINDOW w AS (
     PATTERN (A? B C)
     DEFINE A AS v % 4 = 1, B AS v % 4 = 2, C AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Exact count {n}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev54 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1489,7 +1439,7 @@ WINDOW w AS (
     PATTERN (A{3} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev54'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1500,10 +1450,9 @@ WINDOW w AS (
     PATTERN (A{3} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Range {n,m}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev55 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1512,7 +1461,7 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev55'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1523,10 +1472,9 @@ WINDOW w AS (
     PATTERN (A{2,4} B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- At least {n,}
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev56 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1535,7 +1483,7 @@ WINDOW w AS (
     PATTERN (A{3,} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev56'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1546,7 +1494,6 @@ WINDOW w AS (
     PATTERN (A{3,} B)
     DEFINE A AS v % 10 <> 0, B AS v % 10 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Regression Tests for Statistics Accuracy
@@ -1554,7 +1501,7 @@ DROP VIEW rpr_v;
 
 -- Verify state count accuracy
 -- Pattern A+ B with 20 rows should show predictable state behavior
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev57 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -1563,7 +1510,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev57'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1574,10 +1521,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Verify context count with known absorption
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev58 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -1586,7 +1532,7 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 IN (1,2,3,4,5,6,7), B AS v % 10 = 8, C AS v % 10 = 9
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev58'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1597,10 +1543,9 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 IN (1,2,3,4,5,6,7), B AS v % 10 = 8, C AS v % 10 = 9
 );');
-DROP VIEW rpr_v;
 
 -- Verify match length with fixed-length pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev59 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -1609,7 +1554,7 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev59'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1620,39 +1565,37 @@ WINDOW w AS (
     PATTERN (A B C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Alternation Pattern Tests
 -- ============================================================
 
 -- Simple alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev60 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN ((A | B) C)
     DEFINE A AS cat = 'A', B AS cat = 'B', C AS cat = 'C'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev60'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
     PATTERN ((A | B) C)
     DEFINE A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C''
 );');
-DROP VIEW rpr_v;
 
 -- Multiple items in alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev61 AS
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1661,11 +1604,11 @@ WINDOW w AS (
         A AS cat = 'A', B AS cat = 'B', C AS cat = 'C',
         D AS cat = 'D', E AS cat = 'E'
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev61'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
-FROM nfa_test
+FROM rpr_nfa_test
 WINDOW w AS (
     ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
     AFTER MATCH SKIP PAST LAST ROW
@@ -1674,10 +1617,9 @@ WINDOW w AS (
         A AS cat = ''A'', B AS cat = ''B'', C AS cat = ''C'',
         D AS cat = ''D'', E AS cat = ''E''
 );');
-DROP VIEW rpr_v;
 
 -- Alternation with quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev62 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -1686,7 +1628,7 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev62'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1697,10 +1639,9 @@ WINDOW w AS (
     PATTERN ((A | B)+ C)
     DEFINE A AS v % 3 = 1, B AS v % 3 = 2, C AS v % 3 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Multiple alternatives (4+)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev63 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1708,7 +1649,7 @@ WINDOW w AS (
     PATTERN (A | B | C | D | E)
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev63'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1718,10 +1659,9 @@ WINDOW w AS (
     PATTERN (A | B | C | D | E)
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );');
-DROP VIEW rpr_v;
 
 -- Alternation at start
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev64 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1729,7 +1669,7 @@ WINDOW w AS (
     PATTERN ((A | B) C D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev64'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1739,10 +1679,9 @@ WINDOW w AS (
     PATTERN ((A | B) C D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Multiple sequential alternations
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev65 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 100) AS s(v)
 WINDOW w AS (
@@ -1750,7 +1689,7 @@ WINDOW w AS (
     PATTERN ((A | B) C (D | E) F)
     DEFINE A AS v % 6 = 0, B AS v % 6 = 1, C AS v % 6 = 2, D AS v % 6 = 3, E AS v % 6 = 4, F AS v % 6 = 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev65'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1760,10 +1699,9 @@ WINDOW w AS (
     PATTERN ((A | B) C (D | E) F)
     DEFINE A AS v % 6 = 0, B AS v % 6 = 1, C AS v % 6 = 2, D AS v % 6 = 3, E AS v % 6 = 4, F AS v % 6 = 5
 );');
-DROP VIEW rpr_v;
 
 -- Quantified alternatives
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev66 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1771,7 +1709,7 @@ WINDOW w AS (
     PATTERN ((A+ | B+) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev66'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1781,10 +1719,9 @@ WINDOW w AS (
     PATTERN ((A+ | B+) C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- Alternation at end
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev67 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1792,7 +1729,7 @@ WINDOW w AS (
     PATTERN (A B (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev67'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1802,11 +1739,10 @@ WINDOW w AS (
     PATTERN (A B (C | D))
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- Nested ALT at start of branch inside outer ALT
 -- Pattern: (A ((B | C) D | E)) - preceding VAR + inner ALT as first branch element
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev68 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -1814,7 +1750,7 @@ WINDOW w AS (
     PATTERN (A ((B | C) D | E))
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev68'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1824,11 +1760,10 @@ WINDOW w AS (
     PATTERN (A ((B | C) D | E))
     DEFINE A AS v % 5 = 0, B AS v % 5 = 1, C AS v % 5 = 2, D AS v % 5 = 3, E AS v % 5 = 4
 );');
-DROP VIEW rpr_v;
 
 -- Nested ALT at end of branch inside outer ALT
 -- Pattern: (C (A | B) | D) - inner ALT is last element in outer branch
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev69 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 20) AS s(v)
 WINDOW w AS (
@@ -1836,7 +1771,7 @@ WINDOW w AS (
     PATTERN (C (A | B) | D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev69'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1846,14 +1781,13 @@ WINDOW w AS (
     PATTERN (C (A | B) | D)
     DEFINE A AS v % 4 = 0, B AS v % 4 = 1, C AS v % 4 = 2, D AS v % 4 = 3
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Group Pattern Tests
 -- ============================================================
 
 -- Simple group
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev70 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1862,7 +1796,7 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev70'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1873,10 +1807,9 @@ WINDOW w AS (
     PATTERN ((A B)+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Group with bounded quantifier
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev71 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1885,7 +1818,7 @@ WINDOW w AS (
     PATTERN ((A B){2,4})
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev71'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1896,10 +1829,9 @@ WINDOW w AS (
     PATTERN ((A B){2,4})
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Nested groups
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev72 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1908,7 +1840,7 @@ WINDOW w AS (
     PATTERN (((A B){2})+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev72'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1919,10 +1851,9 @@ WINDOW w AS (
     PATTERN (((A B){2})+)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Deep nesting (3+ levels)
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev73 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 40) AS s(v)
 WINDOW w AS (
@@ -1930,7 +1861,7 @@ WINDOW w AS (
     PATTERN ((((A | B)+)+)+)
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev73'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1940,10 +1871,9 @@ WINDOW w AS (
     PATTERN ((((A | B)+)+)+)
     DEFINE A AS v % 2 = 0, B AS v % 2 = 1
 );');
-DROP VIEW rpr_v;
 
 -- Bounded quantifier on alternation
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev74 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1951,7 +1881,7 @@ WINDOW w AS (
     PATTERN ((A | B){2,3} C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev74'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1961,10 +1891,9 @@ WINDOW w AS (
     PATTERN ((A | B){2,3} C)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- Nested groups with quantifiers
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev75 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1972,7 +1901,7 @@ WINDOW w AS (
     PATTERN (((A B)+ C)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev75'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -1982,10 +1911,9 @@ WINDOW w AS (
     PATTERN (((A B)+ C)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- Partial nested quantification
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev76 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 60) AS s(v)
 WINDOW w AS (
@@ -1993,7 +1921,7 @@ WINDOW w AS (
     PATTERN ((A (B C)+)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev76'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2003,14 +1931,13 @@ WINDOW w AS (
     PATTERN ((A (B C)+)*)
     DEFINE A AS v % 3 = 0, B AS v % 3 = 1, C AS v % 3 = 2
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Window Function Combinations
 -- ============================================================
 
 -- count(*) with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev77 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2019,7 +1946,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev77'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2030,10 +1957,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- first_value with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev78 AS
 SELECT first_value(v) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2042,7 +1968,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev78'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT first_value(v) OVER w
@@ -2053,10 +1979,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- last_value with pattern
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev79 AS
 SELECT last_value(v) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2065,7 +1990,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev79'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT last_value(v) OVER w
@@ -2076,10 +2001,9 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- Multiple window functions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev80 AS
 SELECT
     count(*) OVER w,
     first_value(v) OVER w,
@@ -2091,7 +2015,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev80'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT
@@ -2105,14 +2029,13 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v % 5 <> 0, B AS v % 5 = 0
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- DEFINE Expression Variations
 -- ============================================================
 
 -- Complex boolean expressions
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev81 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 50) AS s(v)
 WINDOW w AS (
@@ -2123,7 +2046,7 @@ WINDOW w AS (
         A AS (v % 5 <> 0) AND (v % 3 <> 0),
         B AS (v % 5 = 0) OR (v % 3 = 0)
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev81'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2136,10 +2059,9 @@ WINDOW w AS (
         A AS (v % 5 <> 0) AND (v % 3 <> 0),
         B AS (v % 5 = 0) OR (v % 3 = 0)
 );');
-DROP VIEW rpr_v;
 
 -- Using PREV function
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev82 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 30) AS s(v)
 WINDOW w AS (
@@ -2151,7 +2073,7 @@ WINDOW w AS (
         U AS v > PREV(v),
         D AS v < PREV(v)
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev82'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2165,10 +2087,9 @@ WINDOW w AS (
         U AS v > PREV(v),
         D AS v < PREV(v)
 );');
-DROP VIEW rpr_v;
 
 -- Using NULL comparisons
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev83 AS
 SELECT count(*) OVER w
 FROM (
     SELECT CASE WHEN v % 5 = 0 THEN NULL ELSE v END AS v
@@ -2180,7 +2101,7 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v IS NOT NULL, B AS v IS NULL
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev83'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2194,14 +2115,13 @@ WINDOW w AS (
     PATTERN (A+ B)
     DEFINE A AS v IS NOT NULL, B AS v IS NULL
 );');
-DROP VIEW rpr_v;
 
 -- ============================================================
 -- Large Scale Statistics Verification
 -- ============================================================
 
 -- 500 rows - verify statistics scale correctly
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev84 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -2210,7 +2130,7 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 < 7, B AS v % 10 = 7, C AS v % 10 = 8
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev84'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2221,10 +2141,9 @@ WINDOW w AS (
     PATTERN (A+ B C)
     DEFINE A AS v % 10 < 7, B AS v % 10 = 7, C AS v % 10 = 8
 );');
-DROP VIEW rpr_v;
 
 -- High match count scenario
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev85 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -2233,7 +2152,7 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev85'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2244,10 +2163,9 @@ WINDOW w AS (
     PATTERN (A B)
     DEFINE A AS v % 2 = 1, B AS v % 2 = 0
 );');
-DROP VIEW rpr_v;
 
 -- High skip count scenario
-CREATE TEMP VIEW rpr_v AS
+CREATE VIEW rpr_ev86 AS
 SELECT count(*) OVER w
 FROM generate_series(1, 500) AS s(v)
 WINDOW w AS (
@@ -2261,7 +2179,7 @@ WINDOW w AS (
         D AS v % 100 = 4,
         E AS v % 100 = 5
 );
-SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_v'), E'\n')) AS line WHERE line ~ 'PATTERN';
+SELECT line FROM unnest(string_to_array(pg_get_viewdef('rpr_ev86'), E'\n')) AS line WHERE line ~ 'PATTERN';
 SELECT rpr_explain_filter('
 EXPLAIN (ANALYZE, BUFFERS OFF, COSTS OFF, TIMING OFF, SUMMARY OFF)
 SELECT count(*) OVER w
@@ -2277,8 +2195,4 @@ WINDOW w AS (
         D AS v % 100 = 4,
         E AS v % 100 = 5
 );');
-DROP VIEW rpr_v;
 
--- Cleanup
-DROP TABLE nfa_test;
-DROP TABLE nfa_complex;
-- 
2.50.1 (Apple Git-155)


From 81a9b83b9d61704e85d97bc79b10ac6a00d73def Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 15:53:33 +0900
Subject: [PATCH 5/8] Disable run condition pushdown for RPR windows


diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 90275e25872..b67c35af39a 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2453,6 +2453,17 @@ find_window_run_conditions(Query *subquery, AttrNumber attno,
 	wclause = (WindowClause *) list_nth(subquery->windowClause,
 										wfunc->winref - 1);
 
+	/*
+	 * If a DEFINE clause exists, we cannot push down a run condition. In the
+	 * case, a window partition (or frame) is divided into multiple reduced
+	 * frames and each frame should be evaluated to the end of the partition
+	 * (or full frame end). This means we cannot apply the run condition
+	 * optimization because it stops evaluation window functions in certain
+	 * cases.
+	 */
+	if (wclause->defineClause != NIL)
+		return false;
+
 	req.type = T_SupportRequestWFuncMonotonic;
 	req.window_func = wfunc;
 	req.window_clause = wclause;
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index ae6d9c9b937..a7c536625f1 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -2465,7 +2465,9 @@ WHERE cnt > 0
 ORDER BY id;
  id | val | cnt 
 ----+-----+-----
-(0 rows)
+  2 |  20 |   2
+  4 |  40 |   1
+(2 rows)
 
 -- Nested subqueries
 SELECT *
-- 
2.50.1 (Apple Git-155)


From 08910348e4c3770880fa098b50596805b15e38f7 Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 15:59:45 +0900
Subject: [PATCH 6/8] Disable frame optimization for RPR windows


diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 04faf919033..29a02f3affb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -5907,6 +5907,14 @@ optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists)
 		if (wflists->windowFuncs[wc->winref] == NIL)
 			continue;
 
+		/*
+		 * If a DEFINE clause exists, do not let support functions replace the
+		 * frame with a non-RPR-compatible one.  RPR windows require ROWS
+		 * BETWEEN CURRENT ROW AND ...
+		 */
+		if (wc->defineClause != NIL)
+			continue;
+
 		foreach(lc2, wflists->windowFuncs[wc->winref])
 		{
 			SupportRequestOptimizeWindowClause req;
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index 3c70a12874a..ef184b7950b 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -3821,3 +3821,117 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
+--
+-- Planner optimization: optimize_window_clauses must not alter RPR frame
+--
+-- optimize_window_clauses() replaces frame options via prosupport functions.
+-- Affected functions: row_number, rank, dense_rank, percent_rank, cume_dist,
+-- ntile.  All would change the frame to ROWS UNBOUNDED PRECEDING, breaking
+-- RPR's required ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING.
+-- Test with row_number() as representative case.
+--
+-- Without RPR: row_number() frame is optimized to ROWS UNBOUNDED PRECEDING
+CREATE VIEW rpr_ev87 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+);
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev87;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Subquery Scan on rpr_ev87
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS UNBOUNDED PRECEDING)
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(6 rows)
+
+-- With RPR: frame must remain ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+CREATE VIEW rpr_ev88 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE
+        B AS v > PREV(v)
+);
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev88;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on rpr_ev88
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Pattern: a b+
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(7 rows)
+
+--
+-- Planner optimization: find_window_run_conditions must not push down
+-- RPR window function results as Run Conditions.
+--
+-- find_window_run_conditions() pushes WHERE filters on monotonic window
+-- functions into WindowAgg as Run Conditions for early termination.
+-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
+-- FOLLOWING), the monotonic direction determines which operators trigger
+-- Run Condition pushdown:
+--   INCREASING (<=): row_number, rank, dense_rank, percent_rank,
+--                    cume_dist, ntile
+--   DECREASING (>):  count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
+-- RPR window function results are match-dependent, not monotonic.
+-- Test with count(*) > 0 as representative case.
+--
+-- Without RPR: count(*) > 0 is pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    )
+) t WHERE cnt > 0;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on t
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Run Condition: (count(*) OVER w > 0)
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(7 rows)
+
+-- With RPR: count(*) > 0 must not be pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (A B+)
+        DEFINE
+            B AS v > PREV(v)
+    )
+) t WHERE cnt > 0;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on t
+   Filter: (t.cnt > 0)
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Pattern: a b+
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(8 rows)
+
diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql
index 8e22382a68e..640d328957b 100644
--- a/src/test/regress/sql/rpr_explain.sql
+++ b/src/test/regress/sql/rpr_explain.sql
@@ -2196,3 +2196,81 @@ WINDOW w AS (
         E AS v % 100 = 5
 );');
 
+--
+-- Planner optimization: optimize_window_clauses must not alter RPR frame
+--
+-- optimize_window_clauses() replaces frame options via prosupport functions.
+-- Affected functions: row_number, rank, dense_rank, percent_rank, cume_dist,
+-- ntile.  All would change the frame to ROWS UNBOUNDED PRECEDING, breaking
+-- RPR's required ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING.
+-- Test with row_number() as representative case.
+--
+
+-- Without RPR: row_number() frame is optimized to ROWS UNBOUNDED PRECEDING
+CREATE VIEW rpr_ev87 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+);
+
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev87;
+
+-- With RPR: frame must remain ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+CREATE VIEW rpr_ev88 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE
+        B AS v > PREV(v)
+);
+
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev88;
+
+--
+-- Planner optimization: find_window_run_conditions must not push down
+-- RPR window function results as Run Conditions.
+--
+-- find_window_run_conditions() pushes WHERE filters on monotonic window
+-- functions into WindowAgg as Run Conditions for early termination.
+-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
+-- FOLLOWING), the monotonic direction determines which operators trigger
+-- Run Condition pushdown:
+--   INCREASING (<=): row_number, rank, dense_rank, percent_rank,
+--                    cume_dist, ntile
+--   DECREASING (>):  count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
+-- RPR window function results are match-dependent, not monotonic.
+-- Test with count(*) > 0 as representative case.
+--
+
+-- Without RPR: count(*) > 0 is pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    )
+) t WHERE cnt > 0;
+
+-- With RPR: count(*) > 0 must not be pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (A B+)
+        DEFINE
+            B AS v > PREV(v)
+    )
+) t WHERE cnt > 0;
+
-- 
2.50.1 (Apple Git-155)


From a276a393cae83a4839ff7482fa5a6d3e9fde49cb Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 22:17:29 +0900
Subject: [PATCH] Add stock scenario tests for RPR pattern matching


diff --git a/src/test/regress/data/stock.data b/src/test/regress/data/stock.data
new file mode 100644
index 00000000000..645bb643df7
--- /dev/null
+++ b/src/test/regress/data/stock.data
@@ -0,0 +1,1632 @@
+1	1	364.750	15800	0.000	0.000	0.000
+1	2	368.250	21200	0.000	0.000	0.000
+1	3	368.500	24400	0.000	0.000	0.000
+1	4	368.750	22900	0.000	0.000	0.000
+1	5	369.500	35400	0.000	0.000	0.000
+1	6	369.000	29300	0.000	0.000	0.000
+1	7	367.750	19000	0.000	0.000	0.000
+1	8	374.125	37200	0.000	0.000	0.000
+2	9	343.250	56800	0.000	0.000	0.000
+2	10	338.500	45200	0.000	0.000	0.000
+2	11	338.750	40900	0.000	0.000	0.000
+2	12	344.750	35200	0.000	0.000	0.000
+2	13	349.750	28500	0.000	0.000	0.000
+2	14	346.000	25600	0.000	0.000	0.000
+3	15	349.000	24900	0.000	0.000	0.000
+3	16	349.250	20700	0.000	0.000	0.000
+3	17	349.750	22200	0.000	0.000	0.000
+3	18	351.250	19600	0.000	0.000	0.000
+3	19	352.000	32500	0.000	0.000	0.000
+3	20	349.500	32200	0.000	0.000	0.000
+3	21	344.500	18600	0.000	0.000	0.000
+3	22	345.000	61200	0.000	0.000	0.000
+4	23	238.250	37100	0.000	0.000	0.000
+4	24	230.625	61500	0.000	0.000	0.000
+4	25	223.000	161700	0.000	0.000	0.000
+4	26	229.875	55300	0.000	0.000	0.000
+4	27	235.250	38500	0.000	0.000	0.000
+5	28	310.000	36000	0.000	0.000	0.000
+5	29	311.750	26800	0.000	0.000	0.000
+5	30	312.750	31900	0.000	0.000	0.000
+5	31	314.750	21100	0.000	0.000	0.000
+5	32	315.500	27400	0.000	0.000	0.000
+5	33	316.500	40700	0.000	0.000	0.000
+5	34	321.750	53500	0.000	0.000	0.000
+5	35	326.000	48700	0.000	0.000	0.000
+5	36	322.500	46000	0.000	0.000	0.000
+5	37	317.500	35600	0.000	0.000	0.000
+5	38	325.500	34000	0.000	0.000	0.000
+5	39	327.250	30700	0.000	0.000	0.000
+5	40	327.750	36700	0.000	0.000	0.000
+5	41	328.625	23200	0.000	0.000	0.000
+5	42	330.500	29700	0.000	0.000	0.000
+5	43	336.500	39200	0.000	0.000	0.000
+5	44	340.750	34400	0.000	0.000	0.000
+5	45	338.500	32200	0.000	0.000	0.000
+6	46	353.000	27800	0.000	0.000	0.000
+6	47	352.000	55600	0.000	0.000	0.000
+6	48	355.000	31300	0.000	0.000	0.000
+6	49	356.000	26900	0.000	0.000	0.000
+6	50	358.000	18900	0.000	0.000	0.000
+6	51	361.250	26400	0.000	0.000	0.000
+7	52	311.750	25100	0.000	0.000	0.000
+7	53	314.000	37900	0.000	0.000	0.000
+7	54	317.000	34000	0.000	0.000	0.000
+7	55	317.750	23700	0.000	0.000	0.000
+7	56	319.000	23400	0.000	0.000	0.000
+7	57	316.500	24200	0.000	0.000	0.000
+8	58	316.000	37200	0.000	0.000	0.000
+8	59	315.000	31500	0.000	0.000	0.000
+8	60	302.000	124700	0.000	0.000	0.000
+8	61	300.000	196100	0.000	0.000	0.000
+8	62	298.000	70000	0.000	0.000	0.000
+8	63	294.500	88200	0.000	0.000	0.000
+8	64	296.500	53800	0.000	0.000	0.000
+8	65	299.000	47900	0.000	0.000	0.000
+8	66	301.750	33300	0.000	0.000	0.000
+8	67	299.500	31800	0.000	0.000	0.000
+9	68	299.500	76700	0.000	0.000	0.000
+9	69	296.750	41900	0.000	0.000	0.000
+9	70	291.250	46700	0.000	0.000	0.000
+9	71	293.750	31100	0.000	0.000	0.000
+9	72	290.000	48900	0.000	0.000	0.000
+9	73	287.000	56300	0.000	0.000	0.000
+9	74	287.000	40700	0.000	0.000	0.000
+9	75	285.500	27900	0.000	0.000	0.000
+9	76	284.000	27400	0.000	0.000	0.000
+9	77	286.000	23500	0.000	0.000	0.000
+9	78	291.250	38000	0.000	0.000	0.000
+9	79	296.750	42900	0.000	0.000	0.000
+9	80	295.000	19500	0.000	0.000	0.000
+9	81	314.000	102900	0.000	0.000	0.000
+9	82	315.750	84100	0.000	0.000	0.000
+10	83	302.000	44600	0.000	0.000	0.000
+10	84	301.000	45000	0.000	0.000	0.000
+10	85	294.750	27900	0.000	0.000	0.000
+10	86	293.500	17600	0.000	0.000	0.000
+10	87	291.750	21600	0.000	0.000	0.000
+10	88	290.500	30300	0.000	0.000	0.000
+10	89	291.750	22700	0.000	0.000	0.000
+10	90	298.750	16200	0.000	0.000	0.000
+10	91	302.250	49100	0.000	0.000	0.000
+10	92	305.500	35500	0.000	0.000	0.000
+10	93	309.750	63200	0.000	0.000	0.000
+10	94	309.500	43200	0.000	0.000	0.000
+11	95	383.750	19300	0.000	0.000	0.000
+11	96	389.500	28900	0.000	0.000	0.000
+11	97	394.500	41700	0.000	0.000	0.000
+11	98	396.250	28200	0.000	0.000	0.000
+11	99	401.000	55000	0.000	0.000	0.000
+11	100	402.000	41100	0.000	0.000	0.000
+11	101	403.000	38500	0.000	0.000	0.000
+11	102	404.000	33100	0.000	0.000	0.000
+11	103	404.000	33800	0.000	0.000	0.000
+12	104	388.000	25800	0.000	0.000	0.000
+12	105	388.500	44900	0.000	0.000	0.000
+12	106	390.000	38400	0.000	0.000	0.000
+12	107	390.750	23200	0.000	0.000	0.000
+12	108	396.500	25100	0.000	0.000	0.000
+12	109	398.750	38000	0.000	0.000	0.000
+12	110	398.000	33600	0.000	0.000	0.000
+12	111	394.500	54000	0.000	0.000	0.000
+12	112	397.000	20900	0.000	0.000	0.000
+13	113	398.125	28500	0.000	396.000	401.000
+13	114	394.500	39900	0.000	392.500	397.500
+13	115	390.000	37600	0.000	390.000	393.750
+13	116	389.500	31000	0.000	386.000	392.750
+13	117	385.500	25800	0.000	385.500	389.750
+13	118	390.500	18000	0.000	385.000	390.500
+13	119	391.750	14600	0.000	391.500	395.500
+13	120	399.000	27800	0.000	392.250	399.000
+13	121	402.000	46500	0.000	399.000	403.000
+13	122	409.000	36300	0.000	404.500	409.500
+13	123	409.250	54500	0.000	409.250	413.750
+13	124	411.750	46200	0.000	406.000	412.500
+13	125	418.750	60200	0.000	412.750	419.500
+13	126	418.750	32700	0.000	416.000	420.000
+14	127	411.500	24800	0.000	411.250	415.500
+14	128	415.250	53000	0.000	412.000	423.000
+14	129	420.000	49700	0.000	417.250	425.000
+14	130	423.250	68700	0.000	422.750	429.000
+14	131	422.500	45400	0.000	420.750	424.750
+14	132	424.000	20900	0.000	420.750	424.250
+14	133	428.875	37600	0.000	427.000	430.000
+14	134	439.000	59800	0.000	427.250	439.000
+15	135	448.750	29300	0.000	444.750	449.500
+15	136	444.000	27400	0.000	444.000	449.750
+15	137	443.500	30900	0.000	440.500	448.500
+15	138	437.875	26500	0.000	437.500	442.875
+15	139	437.750	29000	0.000	433.500	440.000
+15	140	430.500	27500	0.000	430.250	441.250
+15	141	425.500	52300	0.000	424.250	428.880
+15	142	428.000	59900	0.000	419.500	429.000
+15	143	432.500	34600	0.000	427.250	435.000
+15	144	437.500	33100	0.000	432.000	438.500
+15	145	434.500	24300	0.000	434.000	437.750
+15	146	431.500	28500	0.000	430.500	437.500
+15	147	428.000	15500	0.000	427.000	432.750
+15	148	424.250	44900	0.000	424.000	428.750
+15	149	423.000	34000	0.000	420.000	426.500
+15	150	422.875	37800	0.000	420.000	424.000
+15	151	423.500	24000	0.000	423.250	428.000
+15	152	429.500	23200	0.000	420.500	429.500
+15	153	434.250	31800	0.000	429.750	435.000
+15	154	430.250	36500	0.000	426.500	436.250
+16	155	309.750	41400	0.000	307.125	310.750
+16	156	317.000	44500	0.000	309.750	317.000
+16	157	315.000	39900	0.000	313.125	319.750
+16	158	322.000	40600	0.000	318.500	322.500
+16	159	324.250	35300	0.000	321.000	325.250
+16	160	323.000	32300	0.000	322.500	327.000
+16	161	329.250	47700	0.000	323.250	330.000
+16	162	329.500	56700	0.000	328.500	333.500
+16	163	321.000	45400	0.000	320.000	328.500
+17	164	298.000	31900	0.000	290.500	298.000
+17	165	272.000	165000	0.000	270.750	285.125
+17	166	259.500	290400	0.000	252.750	261.000
+17	167	270.750	136000	0.000	266.000	270.750
+17	168	268.750	96600	0.000	266.000	271.750
+17	169	260.000	99100	0.000	258.000	264.500
+17	170	251.500	100900	0.000	250.500	260.750
+17	171	259.500	78100	0.000	253.000	261.000
+17	172	260.500	64600	0.000	260.500	267.500
+17	173	260.000	51400	0.000	259.000	264.500
+17	174	258.000	40800	0.000	257.000	261.000
+17	175	257.750	39400	0.000	255.000	257.750
+17	176	255.500	47500	0.000	253.500	257.500
+17	177	248.500	147400	0.000	247.250	254.000
+17	178	251.500	107100	0.000	245.000	251.500
+17	179	259.500	70100	0.000	252.500	262.000
+17	180	261.750	49800	0.000	255.000	263.500
+17	181	274.000	146700	0.000	270.500	284.500
+17	182	281.000	82800	0.000	277.500	283.750
+17	183	282.000	52000	0.000	281.000	284.500
+17	184	279.250	39600	0.000	278.000	280.250
+18	185	248.000	38000	0.000	247.750	250.000
+18	186	246.500	25100	0.000	244.000	248.250
+18	187	247.000	27100	0.000	245.000	248.750
+18	188	244.375	30500	0.000	244.000	248.000
+18	189	243.250	31600	0.000	240.500	244.250
+18	190	246.250	34900	0.000	245.250	248.500
+18	191	245.250	35300	0.000	245.130	249.750
+18	192	241.250	30700	0.000	240.250	244.500
+19	193	240.500	34800	0.000	237.500	243.250
+19	194	246.500	43600	0.000	236.500	246.500
+19	195	249.500	68300	0.000	246.500	251.500
+19	196	250.000	35100	0.000	249.125	254.000
+19	197	248.750	56400	0.000	247.000	251.500
+19	198	249.250	32100	0.000	246.750	250.500
+19	199	247.500	29300	0.000	247.375	250.750
+19	200	245.750	20000	0.000	245.250	247.500
+19	201	246.250	22500	0.000	245.125	246.750
+19	202	243.000	38800	0.000	242.500	247.750
+20	203	182.250	84300	0.000	176.500	183.500
+20	204	179.000	63300	0.000	179.000	185.500
+20	205	173.000	59200	0.000	171.750	178.500
+20	206	171.500	69600	0.000	170.250	175.250
+20	207	165.125	70500	0.000	165.125	172.250
+20	208	156.500	113200	0.000	155.250	165.000
+20	209	152.000	131000	0.000	150.500	157.250
+20	210	159.625	109100	0.000	150.500	159.750
+20	211	161.880	87800	0.000	161.750	168.500
+20	212	166.500	75800	0.000	158.000	166.500
+20	213	170.500	95900	0.000	169.250	173.750
+20	214	171.500	80600	0.000	166.500	173.500
+20	215	169.250	59500	0.000	168.250	175.000
+20	216	166.750	47000	0.000	164.750	168.000
+20	217	165.000	59700	0.000	165.000	171.000
+20	218	164.000	25800	0.000	160.250	164.000
+20	219	160.875	51900	0.000	160.000	166.750
+20	220	159.000	84100	0.000	153.500	159.500
+20	221	160.250	125800	0.000	154.250	161.750
+20	222	159.500	63900	0.000	158.500	163.500
+20	223	157.750	56100	0.000	155.625	160.500
+20	224	156.250	74200	0.000	154.250	159.500
+20	225	164.125	60900	0.000	159.250	164.750
+20	226	162.000	87000	0.000	160.750	164.500
+21	227	162.250	51800	0.000	161.750	166.000
+21	228	164.250	65300	0.000	160.130	164.250
+21	229	168.000	48900	0.000	166.000	168.875
+21	230	168.875	45100	0.000	168.250	171.375
+21	231	167.500	78000	0.000	166.750	171.250
+21	232	166.625	53000	0.000	165.500	170.250
+21	233	165.750	49000	0.000	163.500	166.500
+21	234	163.250	58700	0.000	163.250	167.125
+21	235	168.500	56700	0.000	162.630	168.500
+22	236	212.000	89200	0.000	212.000	217.750
+22	237	207.500	117700	0.000	206.000	209.750
+22	238	212.000	95100	0.000	207.500	212.500
+22	239	212.250	74000	0.000	211.500	214.000
+22	240	215.500	68400	0.000	211.880	215.750
+22	241	220.000	79200	0.000	216.000	220.000
+23	242	222.500	45700	0.000	222.250	224.630
+23	243	223.250	43000	0.000	221.500	223.500
+23	244	224.250	47700	0.000	223.500	225.250
+23	245	226.500	43800	0.000	223.380	226.500
+23	246	230.000	88800	0.000	226.250	230.000
+23	247	229.380	118000	0.000	229.000	230.880
+23	248	228.500	127100	0.000	228.375	231.000
+23	249	230.380	136400	0.000	228.500	230.750
+24	250	249.880	97500	0.000	249.630	255.000
+24	251	251.000	90400	0.000	248.130	252.250
+24	252	256.500	90400	0.000	251.000	257.000
+24	253	257.750	111000	0.000	257.625	259.750
+24	254	259.250	87200	0.000	256.000	259.250
+24	255	261.000	111100	0.000	258.250	261.250
+24	256	263.750	75900	0.000	261.250	264.500
+24	257	258.000	121100	0.000	257.750	263.250
+24	258	254.000	125500	0.000	254.000	257.250
+24	259	253.500	78400	0.000	252.750	257.750
+24	260	258.000	74700	0.000	253.000	258.000
+25	261	279.750	62700	0.000	277.130	280.000
+25	262	277.625	85500	0.000	276.750	280.375
+25	263	276.630	99800	0.000	276.630	280.380
+25	264	276.250	113600	0.000	276.000	279.500
+25	265	274.250	187100	0.000	272.750	276.380
+25	266	272.000	105300	0.000	272.000	275.500
+25	267	270.500	111000	0.000	268.000	272.750
+25	268	271.000	66000	0.000	270.500	272.500
+25	269	272.500	43000	0.000	270.250	272.500
+25	270	273.000	56200	0.000	272.000	273.880
+25	271	275.500	55200	0.000	273.750	275.500
+25	272	272.250	73400	0.000	272.000	275.500
+26	273	262.250	89000	0.000	262.000	267.750
+26	274	259.500	146100	0.000	257.500	261.750
+26	275	260.250	90300	0.000	257.500	260.625
+26	276	261.000	74100	0.000	258.880	261.880
+26	277	264.500	44400	0.000	260.250	264.880
+26	278	262.000	78900	0.000	261.250	264.500
+27	279	272.500	132000	0.000	270.130	272.500
+27	280	272.000	79800	0.000	271.375	272.625
+27	281	271.130	75600	0.000	270.750	272.880
+27	282	270.000	113800	0.000	269.880	271.500
+27	283	264.000	179500	0.000	263.500	270.500
+27	284	258.000	215300	0.000	257.500	263.250
+27	285	258.250	154200	0.000	256.500	259.625
+27	286	259.500	95600	0.000	257.500	260.500
+27	287	261.250	96500	0.000	259.250	262.000
+27	288	259.750	119000	0.000	259.250	261.625
+27	289	258.750	81500	0.000	257.130	259.750
+28	290	257.380	53700	0.000	257.250	259.250
+28	291	256.000	52200	0.000	255.375	257.500
+28	292	257.750	76200	0.000	255.630	258.630
+28	293	258.750	93400	0.000	257.380	259.500
+28	294	258.750	99000	0.000	258.500	259.630
+28	295	257.250	123100	0.000	255.750	257.500
+28	296	260.500	188300	0.000	257.000	261.000
+28	297	260.250	103300	0.000	259.000	261.250
+29	298	264.250	110100	0.000	261.630	264.380
+29	299	266.000	98100	0.000	264.250	266.750
+29	300	267.500	146100	0.000	266.625	268.000
+29	301	270.250	199400	0.000	267.500	270.250
+29	302	270.250	89800	0.000	269.000	270.625
+29	303	271.000	78600	0.000	269.250	271.250
+29	304	272.750	107600	0.000	269.880	273.500
+29	305	273.500	119200	0.000	272.750	275.250
+29	306	273.500	0	0.000	0.000	0.000
+30	307	236.250	146100	0.000	236.250	242.000
+30	308	239.500	358100	0.000	235.500	239.500
+30	309	243.500	241500	0.000	240.500	244.000
+30	310	251.125	304800	0.000	248.375	253.625
+30	311	251.250	231000	0.000	248.875	253.000
+30	312	253.000	152800	0.000	249.625	254.000
+30	313	253.500	175100	0.000	252.500	257.880
+30	314	254.000	141800	0.000	252.125	255.500
+30	315	258.000	110000	0.000	253.000	258.250
+30	316	261.750	374200	0.000	261.250	264.000
+30	317	262.380	275100	0.000	261.380	265.000
+30	318	261.500	169700	0.000	259.875	261.750
+31	319	262.500	87100	0.000	262.250	266.500
+31	320	258.250	91100	0.000	257.750	262.500
+31	321	260.880	105600	0.000	258.250	262.000
+31	322	261.750	85000	0.000	258.500	261.880
+31	323	259.630	68400	0.000	259.000	262.250
+31	324	257.250	45500	0.000	257.250	260.250
+31	325	256.750	39900	0.000	255.000	257.500
+31	326	258.000	102400	0.000	255.250	259.500
+32	327	268.125	180700	0.000	267.750	272.000
+32	328	270.250	80900	0.000	267.000	270.250
+32	329	271.000	107200	0.000	267.750	271.000
+32	330	273.000	149400	0.000	269.250	273.000
+32	331	273.500	262900	0.000	273.000	276.750
+32	332	276.250	150300	0.000	273.000	276.250
+32	333	278.630	172800	0.000	275.500	279.500
+32	334	281.000	132700	0.000	278.750	281.500
+32	335	279.125	178400	0.000	279.000	281.750
+33	336	311.000	96900	0.000	308.130	311.000
+33	337	308.250	85000	0.000	307.625	311.000
+33	338	305.750	119200	0.000	304.500	306.625
+33	339	303.500	133200	0.000	303.000	306.500
+33	340	298.500	168400	0.000	297.130	299.750
+33	341	299.630	77900	0.000	299.130	300.630
+33	342	301.750	91100	0.000	300.130	301.880
+33	343	306.630	78700	0.000	299.750	307.000
+33	344	308.125	132600	0.000	306.750	310.000
+33	345	305.500	82400	0.000	304.000	308.250
+34	346	308.875	80600	0.000	308.250	312.875
+34	347	306.750	124000	0.000	306.250	309.250
+34	348	310.000	123300	0.000	307.000	310.250
+34	349	310.250	102600	0.000	309.750	312.875
+34	350	312.000	80400	0.000	309.880	312.250
+34	351	308.880	115900	0.000	308.500	312.000
+34	352	310.500	98300	0.000	307.500	310.500
+34	353	314.250	116900	0.000	309.750	314.250
+34	354	315.625	118600	0.000	314.250	317.500
+34	355	318.000	98700	0.000	315.000	318.500
+34	356	319.750	149000	0.000	318.000	321.500
+34	357	311.250	121900	0.000	311.000	317.500
+34	358	309.000	84400	0.000	308.130	309.880
+34	359	312.000	126500	0.000	308.625	312.250
+35	360	78.000	512100	0.000	77.500	78.500
+35	361	75.375	607700	0.000	75.250	78.000
+35	362	74.625	628300	0.000	73.750	75.250
+35	363	74.250	517300	0.000	73.875	75.000
+35	364	72.750	358300	0.000	72.625	74.000
+35	365	72.630	394600	0.000	72.250	73.500
+35	366	73.000	599200	0.000	72.130	73.750
+35	367	73.500	450200	0.000	72.875	74.500
+35	368	74.130	304400	0.000	73.880	74.250
+35	369	73.875	289200	0.000	73.375	74.500
+36	370	72.250	498300	0.000	69.875	72.375
+36	371	71.630	476800	0.000	71.000	72.500
+36	372	71.250	282600	0.000	71.000	71.880
+36	373	71.630	350300	0.000	70.880	71.880
+36	374	70.880	261500	0.000	70.500	71.630
+36	375	70.500	531700	0.000	70.380	71.130
+36	376	69.625	391900	0.000	69.500	70.750
+36	377	69.625	337000	0.000	69.125	70.500
+36	378	70.000	338900	0.000	69.500	70.625
+36	379	70.000	393400	0.000	69.625	70.125
+36	380	69.630	293200	0.000	69.630	70.250
+36	381	69.500	205300	0.000	69.500	70.000
+36	382	70.000	268300	0.000	69.750	70.000
+36	383	68.630	381500	0.000	68.500	69.880
+36	384	68.250	693200	0.000	67.250	68.500
+36	385	67.630	316100	0.000	67.500	68.250
+36	386	67.750	406600	0.000	67.380	68.000
+36	387	67.500	305800	0.000	67.375	67.875
+36	388	66.500	466500	0.000	66.125	67.750
+36	389	66.250	652800	0.000	65.750	66.625
+36	390	66.750	399300	0.000	66.125	66.875
+36	391	67.880	502100	0.000	66.500	68.380
+36	392	68.500	538900	0.000	68.380	69.250
+36	393	67.880	407700	0.000	67.750	68.500
+36	394	67.750	396500	0.000	67.750	68.630
+36	395	71.250	439900	0.000	67.625	71.375
+36	396	70.000	626100	0.000	69.380	71.000
+37	397	65.000	208900	0.000	64.500	65.125
+37	398	65.380	291800	0.000	64.880	65.380
+37	399	65.750	377600	0.000	65.500	66.380
+37	400	67.250	283500	0.000	65.380	67.250
+37	401	66.630	582400	0.000	66.380	67.630
+37	402	66.375	257200	0.000	66.125	66.625
+37	403	65.625	393900	0.000	65.625	66.750
+37	404	65.250	295500	0.000	65.130	65.750
+37	405	65.000	329700	0.000	64.630	65.250
+37	406	66.000	412400	0.000	64.880	66.130
+38	407	64.875	176300	0.000	64.000	64.875
+38	408	64.875	163300	0.000	64.375	65.125
+38	409	64.750	253800	0.000	64.250	65.000
+38	410	64.250	279200	0.000	64.000	64.630
+38	411	64.380	308600	0.000	64.000	64.630
+38	412	62.500	412000	0.000	62.500	64.500
+38	413	63.500	610300	0.000	61.250	63.500
+38	414	63.380	472800	0.000	63.000	63.750
+39	415	54.625	512900	0.000	53.375	54.750
+39	416	54.250	504900	0.000	54.000	55.250
+39	417	53.500	651700	0.000	53.500	55.000
+39	418	53.000	334100	0.000	52.750	53.750
+39	419	52.000	579900	0.000	51.875	53.500
+39	420	51.125	672300	0.000	51.000	52.875
+39	421	51.250	451000	0.000	50.380	51.630
+39	422	51.250	411900	0.000	50.875	51.750
+39	423	51.250	663800	0.000	50.500	51.625
+39	424	53.875	821800	0.000	52.000	54.500
+39	425	53.500	761400	0.000	53.250	54.375
+40	426	66.000	437200	0.000	64.250	66.000
+40	427	65.130	553000	0.000	64.750	66.130
+40	428	64.880	449000	0.000	64.380	65.380
+40	429	64.750	397400	0.000	64.250	65.000
+40	430	64.380	375800	0.000	63.880	64.630
+40	431	65.000	270800	0.000	64.000	65.250
+40	432	65.125	410600	0.000	64.375	65.500
+40	433	65.500	555700	0.000	65.130	66.000
+40	434	65.250	644100	0.000	64.380	65.380
+41	435	56.750	492500	0.000	56.250	56.880
+41	436	56.630	616200	0.000	55.750	56.630
+41	437	56.380	348000	0.000	56.130	56.630
+41	438	55.625	461800	0.000	55.500	56.375
+41	439	55.500	438400	0.000	55.250	55.875
+41	440	56.130	514900	0.000	55.130	56.380
+41	441	57.880	673900	0.000	55.880	58.130
+41	442	58.000	700200	0.000	57.250	58.500
+41	443	58.750	796900	0.000	57.880	59.130
+41	444	60.250	1089200	0.000	59.750	60.750
+41	445	59.880	747600	0.000	59.880	60.500
+42	446	55.875	387300	0.000	55.000	56.750
+42	447	55.500	396600	0.000	55.500	56.500
+42	448	54.880	372500	0.000	54.500	55.380
+42	449	54.130	551600	0.000	53.500	55.130
+42	450	54.000	346100	0.000	53.875	54.500
+42	451	54.375	427300	0.000	54.125	55.000
+42	452	54.500	389800	0.000	53.380	54.630
+42	453	54.625	497600	0.000	54.250	55.250
+42	454	53.625	524200	0.000	53.250	54.250
+43	455	64.625	1058800	0.000	63.375	64.625
+43	456	64.500	689400	0.000	64.250	64.880
+43	457	63.750	1140600	0.000	63.625	65.125
+43	458	63.250	892800	0.000	63.000	63.880
+43	459	63.875	512200	0.000	63.250	63.875
+43	460	64.375	645800	0.000	64.000	64.750
+43	461	65.750	1214100	0.000	64.500	65.750
+43	462	66.000	911700	0.000	65.000	66.130
+43	463	65.125	685600	0.000	64.625	65.750
+43	464	64.880	717200	0.000	64.500	65.250
+43	465	64.125	491600	0.000	64.125	64.625
+43	466	64.250	759200	0.000	64.000	64.750
+43	467	64.500	384600	0.000	64.000	64.750
+43	468	64.750	497500	0.000	64.500	65.250
+43	469	65.125	855600	0.000	64.750	65.250
+43	470	64.875	777200	0.000	64.750	65.125
+43	471	64.750	487900	0.000	64.750	65.130
+43	472	63.875	569800	0.000	63.875	64.875
+43	473	64.380	372400	0.000	64.000	64.750
+44	474	60.000	290100	0.000	60.000	60.630
+44	475	60.380	358800	0.000	59.380	60.750
+44	476	61.000	638300	0.000	60.380	61.250
+44	477	61.500	963900	0.000	59.750	61.630
+44	478	62.500	1247100	0.000	61.380	62.500
+44	479	63.500	985100	0.000	62.880	63.630
+44	480	64.500	1556300	0.000	63.125	65.000
+44	481	66.875	1688100	0.000	64.750	66.875
+44	482	66.750	1578700	0.000	66.375	67.500
+45	483	76.750	1321700	0.000	74.750	76.750
+45	484	75.500	1604100	0.000	75.500	77.630
+45	485	76.380	1254000	0.000	75.250	76.630
+45	486	76.000	887100	0.000	75.630	76.500
+45	487	76.630	490500	0.000	75.750	77.000
+45	488	76.250	868300	0.000	76.250	77.250
+45	489	74.750	831000	0.000	74.500	76.375
+45	490	73.375	997900	0.000	73.250	74.875
+45	491	74.130	747500	0.000	73.380	74.630
+45	492	74.500	614900	0.000	73.250	74.630
+45	493	75.130	752300	0.000	74.630	76.130
+45	494	79.000	1346100	0.000	75.375	79.000
+45	495	80.380	2207600	0.000	78.380	80.380
+45	496	81.130	1342500	0.000	79.250	81.130
+45	497	83.500	1439300	0.000	81.380	84.500
+45	498	82.750	1500400	0.000	82.375	84.375
+46	499	96.130	725400	0.000	95.750	98.000
+46	500	96.250	373700	0.000	96.125	97.125
+46	501	93.000	760600	0.000	92.500	97.000
+46	502	95.625	915100	0.000	92.375	95.625
+46	503	96.000	950900	0.000	95.000	96.750
+46	504	97.625	1024200	0.000	95.500	98.500
+46	505	96.630	1060600	0.000	96.500	98.380
+46	506	98.000	1114200	0.000	96.000	99.250
+46	507	98.000	850900	0.000	97.000	98.380
+47	508	102.000	711800	0.000	101.880	103.250
+47	509	102.625	858000	0.000	101.250	102.875
+47	510	103.250	607100	0.000	102.000	103.375
+47	511	103.875	894200	0.000	102.625	104.000
+47	512	106.625	1651200	0.000	104.625	106.750
+47	513	107.000	1049000	0.000	106.000	107.380
+47	514	108.750	978800	0.000	107.375	109.000
+47	515	109.500	1007500	0.000	107.250	110.000
+47	516	110.125	794100	0.000	109.625	110.500
+47	517	112.630	1020400	0.000	110.000	112.630
+47	518	111.500	929400	0.000	111.000	112.130
+48	519	116.500	424700	0.000	115.625	116.625
+48	520	115.250	668900	0.000	114.000	115.250
+48	521	114.000	722000	0.000	113.125	115.000
+48	522	111.750	802300	0.000	110.750	114.750
+48	523	111.250	673500	0.000	110.500	112.500
+48	524	110.750	538900	0.000	110.250	111.750
+48	525	112.500	813000	0.000	109.750	112.875
+48	526	114.630	665400	0.000	113.000	115.000
+48	527	115.630	843600	0.000	113.380	115.630
+48	528	114.380	647800	0.000	114.250	115.880
+48	529	113.000	404400	0.000	112.875	115.000
+48	530	111.250	734800	0.000	110.250	112.880
+48	531	111.875	890300	0.000	110.250	111.875
+48	532	113.500	803000	0.000	111.250	113.875
+48	533	114.000	662100	0.000	113.000	114.880
+48	534	115.750	573900	0.000	113.630	116.130
+48	535	113.130	791400	0.000	113.000	116.250
+48	536	113.375	831700	0.000	112.625	114.750
+48	537	113.750	672300	0.000	113.130	114.500
+48	538	114.250	419600	0.000	113.630	114.630
+48	539	117.250	737800	0.000	114.125	117.250
+48	540	118.000	971500	0.000	117.000	119.750
+48	541	121.000	1170300	0.000	118.130	121.000
+48	542	121.750	1233400	0.000	121.500	122.875
+48	543	121.130	625500	0.000	120.250	122.250
+49	544	120.875	542900	0.000	120.250	122.750
+49	545	118.000	865300	0.000	117.750	121.380
+49	546	120.000	669800	0.000	117.880	120.880
+49	547	120.250	556200	0.000	119.750	121.000
+49	548	121.000	373100	0.000	120.375	121.250
+49	549	119.250	943300	0.000	118.625	119.625
+50	550	122.880	814300	0.000	121.630	123.500
+50	551	121.750	691100	0.000	121.500	123.250
+50	552	123.125	819700	0.000	121.500	124.250
+50	553	123.750	911500	0.000	122.625	124.875
+50	554	123.875	942300	0.000	123.125	124.500
+50	555	123.000	1070900	0.000	122.875	124.125
+50	556	124.625	1099300	0.000	122.750	124.750
+50	557	126.625	1111900	0.000	124.250	126.625
+50	558	128.625	2097500	0.000	126.000	129.500
+50	559	127.875	1271500	0.000	126.500	128.375
+51	560	129.875	1190200	0.000	128.875	130.125
+51	561	131.130	1391200	0.000	129.130	131.250
+51	562	132.130	1104600	0.000	131.000	132.250
+51	563	132.250	803300	0.000	131.750	132.500
+51	564	134.250	658400	0.000	131.500	134.250
+51	565	133.000	687100	0.000	132.750	134.000
+52	566	126.750	1413200	0.000	125.000	127.250
+52	567	126.500	946400	0.000	125.500	127.000
+52	568	123.880	922600	0.000	123.500	126.500
+52	569	122.250	1474500	0.000	121.750	123.250
+52	570	122.125	803600	0.000	122.000	123.375
+52	571	122.250	650900	0.000	121.880	122.880
+52	572	123.625	858900	0.000	122.375	123.625
+52	573	125.250	1165800	0.000	124.000	125.750
+52	574	126.875	784800	0.000	125.125	127.250
+52	575	127.500	1030600	0.000	127.000	128.250
+52	576	124.500	1354300	0.000	124.000	127.630
+53	577	111.250	1043200	0.000	109.250	111.500
+53	578	112.880	1458900	0.000	110.750	113.500
+53	579	110.000	1241300	0.000	109.875	112.500
+53	580	110.250	1117000	0.000	109.375	111.750
+53	581	111.500	812900	0.000	109.750	111.625
+53	582	112.000	1005000	0.000	111.625	112.875
+53	583	111.000	779400	0.000	110.375	111.500
+53	584	109.250	868500	0.000	109.125	111.625
+54	585	108.000	729600	0.000	107.375	108.625
+54	586	110.250	1455900	0.000	108.500	110.500
+54	587	110.625	1432300	0.000	110.375	112.000
+54	588	111.000	990700	0.000	110.375	111.500
+54	589	112.000	954700	0.000	111.125	112.500
+54	590	113.750	2110500	0.000	112.750	114.250
+54	591	114.000	817600	0.000	112.500	114.000
+54	592	114.750	1241000	0.000	113.500	115.380
+54	593	114.000	914900	0.000	114.000	115.500
+54	594	112.750	881400	0.000	112.375	114.000
+54	595	112.250	836700	0.000	111.500	113.000
+54	596	112.250	694200	0.000	111.750	113.250
+55	597	112.750	770500	0.000	110.630	112.880
+55	598	112.250	931500	0.000	112.000	113.500
+55	599	111.125	720700	0.000	110.500	112.250
+55	600	110.750	1028700	0.000	110.125	111.125
+55	601	109.880	836400	0.000	109.630	111.380
+55	602	110.750	1104400	0.000	109.500	111.625
+55	603	111.875	899400	0.000	110.625	112.250
+55	604	113.250	1090000	0.000	111.875	113.500
+55	605	113.380	1022900	0.000	112.630	114.250
+55	606	113.750	1122500	0.000	112.750	114.630
+55	607	116.500	1463500	0.000	114.000	116.500
+55	608	116.250	1337900	0.000	116.000	117.250
+56	609	107.250	704700	0.000	105.375	107.500
+56	610	107.125	802400	0.000	106.250	107.375
+56	611	105.250	897500	0.000	105.250	107.130
+56	612	104.375	1076900	0.000	104.125	105.625
+56	613	105.250	856100	0.000	104.000	105.750
+56	614	107.250	925700	0.000	104.500	107.250
+56	615	108.130	1047100	0.000	106.750	108.380
+56	616	107.500	625100	0.000	107.125	108.125
+56	617	107.000	895900	0.000	106.250	107.375
+56	618	106.500	810000	0.000	105.500	107.250
+56	619	106.125	931100	0.000	105.125	106.375
+56	620	105.380	710200	0.000	105.130	107.000
+56	621	107.125	1591800	0.000	104.500	107.750
+56	622	108.250	1002700	0.000	106.750	108.750
+56	623	109.000	1059300	0.000	107.875	109.625
+56	624	108.380	610400	0.000	108.250	109.380
+57	625	124.625	929500	0.000	124.625	126.125
+57	626	125.750	1200100	0.000	125.250	126.875
+57	627	125.880	763200	0.000	125.750	126.500
+57	628	126.750	589700	0.000	123.750	126.875
+57	629	127.500	1270600	0.000	126.500	127.750
+57	630	125.750	1668400	0.000	125.375	127.125
+57	631	124.880	1056700	0.000	124.250	125.750
+57	632	123.250	944300	0.000	123.250	125.500
+57	633	124.000	782700	0.000	122.750	124.000
+58	634	124.250	754200	0.000	123.250	124.500
+58	635	123.750	418300	0.000	123.625	124.250
+58	636	123.125	505200	0.000	122.750	124.250
+58	637	123.500	573600	0.000	122.750	124.000
+58	638	123.125	685000	0.000	122.875	123.875
+58	639	121.000	872500	0.000	120.750	122.630
+58	640	120.000	1388100	0.000	119.875	122.250
+58	641	119.750	1007000	0.000	119.000	120.375
+59	642	123.625	1069600	0.000	123.250	124.625
+59	643	124.130	1163300	0.000	123.630	125.000
+59	644	128.250	2138800	0.000	124.630	128.500
+59	645	129.630	2539000	0.000	129.130	130.630
+59	646	132.500	2270200	0.000	129.375	132.625
+59	647	133.130	1825200	0.000	132.380	134.500
+59	648	133.250	1212300	0.000	132.625	134.125
+59	649	134.625	1936500	0.000	133.250	135.250
+59	650	137.000	1128700	0.000	133.750	137.000
+59	651	136.630	1563600	0.000	136.130	137.630
+60	652	130.500	1037200	0.000	128.250	131.125
+60	653	129.375	1118800	0.000	129.000	130.625
+60	654	128.750	1552200	0.000	128.125	130.000
+60	655	127.750	1197800	0.000	127.750	128.750
+60	656	124.500	2057900	0.000	124.500	128.000
+60	657	125.375	1404400	0.000	124.125	125.625
+60	658	126.380	1098400	0.000	125.500	126.630
+60	659	126.625	993400	0.000	126.375	127.625
+60	660	128.125	840800	0.000	126.875	128.250
+60	661	127.750	949000	0.000	127.500	129.500
+61	662	126.500	1280300	0.000	125.500	126.750
+61	663	125.630	1135000	0.000	125.130	126.880
+61	664	125.375	1258800	0.000	124.750	126.375
+61	665	125.000	770500	0.000	124.630	125.630
+61	666	124.750	579400	0.000	124.750	125.625
+61	667	125.750	772900	0.000	125.000	126.250
+61	668	127.125	925700	0.000	125.375	127.250
+61	669	127.880	978300	0.000	127.000	128.250
+61	670	130.130	2239000	0.000	128.500	130.880
+61	671	130.250	1157600	0.000	129.750	130.625
+61	672	128.000	1551200	0.000	127.750	130.875
+62	673	133.000	1215000	0.000	132.380	133.250
+62	674	132.375	857100	0.000	131.875	132.750
+62	675	131.375	829300	0.000	131.250	132.375
+62	676	130.880	675200	0.000	130.750	131.630
+62	677	130.375	921900	0.000	129.750	131.750
+62	678	129.750	939700	0.000	129.250	130.250
+62	679	128.000	1666600	0.000	127.250	129.500
+62	680	128.625	1371900	0.000	127.250	130.000
+62	681	129.125	1265600	0.000	128.250	129.500
+62	682	129.750	1176100	0.000	128.750	130.130
+62	683	128.500	1644500	0.000	128.500	130.630
+63	684	121.130	1753000	0.000	119.380	121.130
+63	685	120.130	1316600	0.000	119.250	120.500
+63	686	119.875	1819000	0.000	119.125	121.625
+63	687	119.375	1268900	0.000	119.250	120.875
+63	688	118.750	1400300	0.000	117.375	119.750
+63	689	119.380	1365100	0.000	118.380	119.750
+63	690	120.875	1125300	0.000	118.750	121.125
+63	691	122.000	1753000	0.000	121.380	123.380
+63	692	123.130	914300	0.000	122.000	123.250
+63	693	124.000	1151500	0.000	123.125	124.250
+63	694	123.750	820500	0.000	123.500	124.500
+64	695	141.750	1638400	0.000	140.250	141.750
+64	696	144.630	1662500	0.000	142.130	144.880
+64	697	146.625	1940200	0.000	144.250	148.000
+64	698	149.000	1985600	0.000	146.625	149.375
+64	699	148.630	1407600	0.000	147.000	149.380
+64	700	150.250	1890100	0.000	149.130	151.500
+64	701	152.250	1586100	0.000	150.000	153.500
+64	702	152.500	1786100	0.000	151.500	154.500
+64	703	152.875	1524100	0.000	151.625	153.875
+64	704	153.750	1105900	0.000	152.375	154.000
+64	705	154.250	2127800	0.000	153.750	156.625
+64	706	154.000	1220900	0.000	153.380	155.130
+64	707	152.750	1011600	0.000	151.250	154.630
+64	708	153.250	562700	0.000	152.125	153.750
+65	709	141.130	1858600	0.000	138.880	141.250
+65	710	141.125	1561600	0.000	140.500	142.000
+65	711	140.000	1229000	0.000	139.500	140.380
+65	712	138.750	887600	0.000	138.000	140.000
+65	713	136.000	1938300	0.000	135.500	138.875
+65	714	136.125	1628600	0.000	135.125	136.375
+65	715	139.380	1630000	0.000	136.000	139.500
+65	716	140.375	2618100	0.000	138.500	141.000
+65	717	143.750	2629100	0.000	140.875	143.875
+65	718	143.880	2251100	0.000	143.500	145.500
+65	719	144.130	1766900	0.000	142.880	144.500
+65	720	139.250	2866100	0.000	138.750	142.880
+65	721	137.380	4379500	0.000	136.250	139.880
+65	722	137.000	2194500	0.000	136.000	138.500
+65	723	138.250	1413100	0.000	135.625	138.500
+66	724	123.625	2431100	0.000	122.375	124.500
+66	725	122.000	1609300	0.000	121.880	125.250
+66	726	120.880	3142200	0.000	119.750	123.000
+66	727	123.000	2239500	0.000	120.375	123.000
+66	728	121.130	2220400	0.000	120.880	123.380
+66	729	121.875	1947600	0.000	120.500	122.375
+66	730	120.250	1484700	0.000	119.875	121.250
+66	731	121.375	1697000	0.000	120.250	121.375
+66	732	120.375	1283900	0.000	120.125	121.500
+67	733	123.130	2107700	0.000	122.500	124.750
+67	734	121.380	2581200	0.000	119.250	122.000
+67	735	121.500	1103400	0.000	120.000	121.880
+67	736	123.125	998200	0.000	121.250	123.250
+67	737	123.750	751400	0.000	122.875	124.250
+67	738	122.750	1130400	0.000	121.875	124.375
+68	739	151.750	1804700	0.000	151.500	154.130
+68	740	154.250	3308900	0.000	149.500	156.000
+68	741	155.250	2945900	0.000	154.625	156.625
+68	742	158.630	3248800	0.000	155.880	159.630
+68	743	160.125	3272200	0.000	157.750	161.500
+68	744	160.630	2385000	0.000	159.130	162.380
+68	745	163.000	1561700	0.000	159.750	163.000
+68	746	166.875	2792500	0.000	164.250	167.625
+68	747	166.750	2187900	0.000	166.000	167.630
+68	748	164.880	1652200	0.000	163.880	166.750
+68	749	163.750	1430500	0.000	163.250	165.875
+68	750	163.000	2269600	0.000	163.000	167.380
+68	751	165.380	1998200	0.000	162.250	166.130
+69	752	159.250	2057900	0.000	158.750	160.880
+69	753	159.380	1668400	0.000	158.500	160.000
+69	754	160.250	2435800	0.000	159.750	161.000
+69	755	163.000	2284000	0.000	159.125	163.250
+69	756	164.375	2386900	0.000	163.000	165.625
+69	757	166.750	1632200	0.000	164.500	166.750
+69	758	169.000	3203300	0.000	167.130	169.630
+69	759	170.380	3299900	0.000	168.000	172.130
+69	760	173.380	2586600	0.000	170.380	174.000
+69	761	173.250	2109200	0.000	172.000	175.000
+70	762	150.500	2630500	0.000	149.500	153.750
+70	763	150.750	3073300	0.000	148.500	151.880
+70	764	154.630	1908400	0.000	151.000	154.750
+70	765	155.250	1868500	0.000	154.750	156.880
+70	766	156.500	1544300	0.000	154.500	157.250
+70	767	151.000	2044200	0.000	151.000	156.375
+71	768	145.250	2922100	0.000	144.130	147.380
+71	769	140.125	3923900	0.000	140.000	145.750
+71	770	135.000	4665000	0.000	132.000	141.250
+71	771	104.000	6384400	0.000	102.000	130.750
+71	772	115.000	0	0.000	110.130	122.000
+71	773	122.750	5386600	0.000	120.000	125.500
+72	774	118.880	2233600	0.000	115.000	119.250
+72	775	115.500	1824700	0.000	115.000	119.500
+72	776	117.250	2798900	0.000	115.250	118.250
+72	777	118.250	1550200	0.000	116.880	119.500
+72	778	118.250	1217000	0.000	116.500	118.375
+72	779	119.500	1646400	0.000	118.750	120.380
+72	780	119.875	630300	0.000	119.000	119.875
+72	781	115.750	1086800	0.000	115.130	117.630
+73	782	111.630	1230300	0.000	110.750	112.130
+73	783	112.250	769000	0.000	111.500	112.750
+73	784	113.380	867400	0.000	112.750	114.000
+73	785	112.875	896800	0.000	112.625	113.750
+73	786	111.500	932200	0.000	110.750	113.500
+73	787	110.250	1489000	0.000	109.500	111.130
+73	788	114.000	1564900	0.000	111.630	114.750
+73	789	112.875	958200	0.000	112.375	113.875
+74	790	114.125	1652700	0.000	112.500	114.375
+74	791	113.500	947400	0.000	113.375	114.125
+74	792	112.750	1100200	0.000	112.500	114.380
+74	793	112.500	2195900	0.000	110.630	112.630
+74	794	113.000	1558100	0.000	111.880	113.000
+74	795	113.380	1187700	0.000	113.130	114.250
+74	796	115.000	1365300	0.000	113.630	115.250
+74	797	114.625	1992000	0.000	114.500	116.500
+74	798	114.000	1582100	0.000	113.625	115.000
+74	799	113.750	2225000	0.000	112.625	114.000
+74	800	115.500	2406400	0.000	113.250	115.750
+74	801	117.375	2209500	0.000	115.750	117.625
+74	802	118.250	2648200	0.000	117.750	119.375
+74	803	117.125	1619800	0.000	117.125	119.125
+75	804	116.380	1333800	0.000	116.000	118.380
+75	805	116.875	1509800	0.000	116.125	117.500
+75	806	117.130	939000	0.000	116.880	117.630
+75	807	117.380	1049800	0.000	116.880	118.250
+75	808	117.750	1708300	0.000	117.375	118.750
+75	809	116.750	1105600	0.000	116.000	117.630
+75	810	116.250	1141200	0.000	115.875	117.000
+75	811	116.000	1173400	0.000	115.000	116.380
+75	812	116.250	946600	0.000	115.375	116.375
+76	813	98.750	1249700	0.000	98.000	99.380
+76	814	99.500	1127900	0.000	98.500	99.750
+76	815	100.125	1129400	0.000	99.375	100.500
+76	816	100.375	638800	0.000	100.250	100.750
+76	817	99.380	1245400	0.000	98.250	100.750
+76	818	98.880	1157700	0.000	98.500	99.380
+76	819	97.750	956500	0.000	97.500	98.625
+76	820	97.630	1001300	0.000	96.880	98.130
+76	821	97.130	1434200	0.000	96.880	98.130
+76	822	99.250	2026900	0.000	97.380	99.500
+77	823	95.375	1005100	0.000	95.125	95.750
+77	824	94.500	682200	0.000	94.250	95.500
+77	825	94.750	1165700	0.000	94.500	95.375
+77	826	94.130	1192300	0.000	94.000	95.130
+77	827	94.130	1637100	0.000	94.000	94.630
+77	828	98.000	1760400	0.000	94.500	98.130
+77	829	98.875	2366000	0.000	98.375	99.500
+77	830	100.000	2418700	0.000	98.880	100.380
+77	831	99.750	1890500	0.000	99.500	101.630
+78	832	106.625	1050400	0.000	106.000	106.750
+78	833	106.500	1716200	0.000	105.880	107.750
+78	834	106.130	868400	0.000	105.880	106.880
+78	835	105.875	752300	0.000	105.250	106.375
+78	836	105.500	782800	0.000	105.250	105.880
+78	837	105.875	934800	0.000	105.125	106.375
+78	838	106.375	645200	0.000	105.875	106.500
+78	839	107.125	921600	0.000	107.000	108.000
+78	840	110.880	4120700	0.000	110.130	111.250
+78	841	111.125	1916900	0.000	109.875	111.250
+78	842	109.875	1531000	0.000	109.500	110.875
+79	843	102.380	1347000	0.000	101.500	103.500
+79	844	105.500	1634700	0.000	101.880	105.500
+79	845	105.880	1609400	0.000	105.500	107.630
+79	846	107.500	1438200	0.000	105.880	107.750
+79	847	107.880	1192900	0.000	107.130	108.380
+79	848	105.000	1077000	0.000	104.750	107.750
+80	849	107.375	1101700	0.000	107.125	109.000
+80	850	107.880	1162000	0.000	106.380	108.250
+80	851	108.250	1570000	0.000	106.000	109.000
+80	852	109.500	901300	0.000	108.625	109.500
+80	853	105.375	1418600	0.000	105.375	108.625
+80	854	103.500	2258600	0.000	102.875	106.625
+80	855	101.000	3113800	0.000	99.750	103.880
+80	856	100.250	2308100	0.000	99.500	102.000
+80	857	99.250	3121400	0.000	96.250	100.875
+80	858	99.380	1690200	0.000	98.250	100.380
+80	859	100.750	1330300	0.000	99.380	101.630
+80	860	105.375	1866500	0.000	101.750	105.375
+80	861	107.625	2658600	0.000	104.625	109.000
+80	862	107.750	1918300	0.000	106.500	108.750
+80	863	106.750	1270700	0.000	106.625	108.250
+81	864	111.500	3389800	0.000	111.250	116.000
+81	865	112.500	1909400	0.000	110.750	112.875
+81	866	113.380	1173800	0.000	112.130	113.750
+81	867	112.875	997900	0.000	112.375	113.125
+81	868	114.375	1471900	0.000	112.625	114.500
+81	869	112.880	1093100	0.000	112.880	114.630
+81	870	111.250	1435200	0.000	111.000	112.500
+81	871	111.500	954900	0.000	110.625	111.750
+81	872	113.500	1617100	0.000	111.500	113.500
+81	873	112.750	2463900	0.000	112.130	114.000
+81	874	113.750	1896000	0.000	112.250	114.630
+81	875	113.875	2084600	0.000	113.375	114.375
+81	876	113.880	341300	0.000	113.750	114.250
+81	877	113.500	396800	0.000	113.500	114.250
+81	878	113.630	714100	0.000	113.000	113.880
+81	879	113.375	728200	0.000	113.250	113.875
+81	880	113.000	461300	0.000	112.880	113.500
+81	881	112.125	1037700	0.000	112.125	113.750
+81	882	112.500	1307400	0.000	112.250	113.880
+81	883	112.130	1085100	0.000	111.880	113.000
+81	884	110.250	1189400	0.000	110.000	111.875
+81	885	109.000	1715500	0.000	108.750	110.375
+81	886	106.880	2078000	0.000	106.750	110.750
+81	887	108.380	962000	0.000	107.250	108.380
+81	888	108.130	608700	0.000	107.750	108.250
+81	889	106.750	1746500	0.000	105.500	107.130
+81	890	107.500	1168100	0.000	106.750	107.750
+81	891	109.130	1582000	0.000	106.630	109.250
+81	892	115.750	4330400	0.000	114.250	116.130
+81	893	117.625	4592500	0.000	114.875	118.250
+82	894	118.000	1963700	0.000	117.750	119.625
+82	895	119.250	2211000	0.000	117.875	120.125
+82	896	121.000	2841200	0.000	119.880	121.630
+82	897	122.625	2441100	0.000	120.500	122.625
+82	898	124.250	2478400	0.000	122.380	125.000
+82	899	124.500	1949200	0.000	123.000	125.380
+82	900	127.000	2524300	0.000	124.630	127.000
+82	901	126.750	1718300	0.000	126.250	127.130
+82	902	126.880	1919100	0.000	125.630	127.630
+83	903	128.500	2532000	0.000	128.380	131.000
+83	904	129.500	1751700	0.000	128.250	129.875
+83	905	132.375	1865300	0.000	129.625	132.500
+83	906	132.750	2372400	0.000	131.500	133.875
+83	907	134.630	1923800	0.000	132.250	135.000
+83	908	135.000	2485400	0.000	133.880	136.250
+83	909	137.375	2738900	0.000	135.375	137.750
+83	910	139.500	2278100	0.000	137.000	139.750
+83	911	137.880	1854200	0.000	137.880	138.750
+84	912	110.630	2214700	0.000	110.130	111.880
+84	913	108.500	3555500	0.000	108.130	111.000
+84	914	106.875	3026800	0.000	105.625	108.375
+84	915	109.250	2086700	0.000	106.750	109.250
+84	916	109.880	2089000	0.000	109.250	110.750
+84	917	109.750	1463800	0.000	109.375	110.750
+84	918	109.500	2109300	0.000	109.000	110.250
+84	919	108.500	1570400	0.000	108.125	109.375
+85	920	98.000	1438800	0.000	97.630	99.380
+85	921	97.250	1909500	0.000	96.750	98.250
+85	922	99.750	1417200	0.000	97.000	99.875
+85	923	99.880	893600	0.000	99.500	100.500
+85	924	101.250	791000	0.000	99.125	101.625
+85	925	104.250	3033600	0.000	99.750	104.750
+85	926	101.625	2025900	0.000	101.375	103.750
+85	927	99.875	1631900	0.000	99.375	101.250
+85	928	100.380	1315700	0.000	99.630	100.380
+86	929	94.625	1973000	92.000	91.875	94.750
+86	930	92.380	2515900	94.000	92.250	95.000
+86	931	91.250	2040100	92.625	91.250	94.250
+86	932	90.875	1611500	91.375	90.625	92.500
+86	933	90.250	1333300	90.750	89.750	90.875
+86	934	92.375	1338400	90.375	90.250	92.500
+86	935	95.380	3103900	94.000	93.500	95.750
+86	936	95.500	2253200	95.125	95.000	97.125
+86	937	96.375	3582000	95.000	92.625	98.125
+86	938	95.250	1209500	96.250	95.250	96.375
+86	939	92.880	1426400	95.250	92.880	95.500
+86	940	95.630	1273400	92.880	92.880	95.630
+87	941	92.250	1502600	91.125	90.000	92.250
+87	942	91.250	1632500	91.750	91.000	92.130
+87	943	89.880	1401700	90.380	89.630	90.880
+87	944	89.250	1724200	90.000	88.625	90.375
+87	945	89.750	945800	89.625	89.125	89.875
+87	946	90.250	1130700	89.750	89.750	90.875
+87	947	92.125	1581500	90.500	89.875	92.375
+87	948	90.750	1954000	92.750	90.130	93.250
+87	949	89.750	1295100	90.500	89.500	91.130
+87	950	89.625	1314500	90.000	89.125	91.125
+87	951	89.630	1148400	90.000	89.380	90.750
+87	952	89.750	1363200	89.750	89.125	90.625
+87	953	89.875	1446100	89.750	89.250	90.375
+87	954	89.380	1056500	89.880	88.750	90.250
+88	955	82.380	1926400	83.380	82.250	84.880
+88	956	84.875	3020100	82.625	82.625	85.000
+88	957	85.125	2275600	85.375	84.625	85.875
+88	958	86.000	2052900	85.630	84.500	86.130
+88	959	87.500	1712300	86.130	85.880	87.500
+88	960	88.250	3316600	89.500	88.125	89.750
+88	961	88.500	2215600	88.750	87.625	88.875
+88	962	88.625	2063100	88.500	87.500	88.625
+88	963	89.500	1792800	88.500	88.250	89.750
+88	964	90.000	2117200	89.630	89.500	90.250
+88	965	90.125	1324900	89.875	89.625	90.125
+88	966	89.250	1475400	90.000	89.000	90.630
+89	967	89.250	1475400	90.000	89.000	90.630
+89	968	88.130	993100	89.130	88.130	89.250
+89	969	88.125	1000900	87.875	87.750	88.750
+89	970	88.500	1108900	88.130	87.380	88.500
+89	971	88.130	1268600	88.880	87.750	88.880
+89	972	90.750	1681900	88.500	88.250	90.750
+89	973	90.625	2074400	91.250	90.375	92.125
+89	974	92.500	2341200	92.500	92.125	93.000
+89	975	93.875	2044900	92.500	92.375	94.000
+90	976	62.880	2541800	62.380	62.380	63.380
+90	977	56.130	12193500	0.000	56.000	59.880
+90	978	51.875	13421200	53.625	50.750	54.875
+90	979	53.000	8025400	0.000	51.250	53.380
+90	980	51.375	7658000	53.125	50.250	53.250
+90	981	48.875	7384600	51.250	48.750	51.625
+90	982	51.750	6115700	50.000	49.000	52.000
+90	983	51.250	4186200	51.625	51.000	52.625
+91	984	46.380	3638900	47.000	45.880	47.000
+91	985	48.625	3420500	46.500	46.500	48.750
+91	986	48.875	3054500	48.750	47.500	49.250
+91	987	49.000	7802400	52.875	48.750	53.250
+91	988	49.625	3307500	49.125	48.250	50.000
+91	989	50.250	3095700	50.000	49.750	50.875
+91	990	51.500	2981500	51.000	50.875	51.625
+91	991	52.625	3039500	52.000	52.000	52.625
+91	992	52.125	2635400	52.250	52.000	53.000
+92	993	42.250	2494200	43.625	42.000	43.625
+92	994	42.375	3922300	41.875	41.125	42.375
+92	995	45.630	6260300	44.880	44.500	45.880
+92	996	44.000	4216700	46.000	43.875	46.375
+93	997	51.875	2331700	52.250	51.500	52.625
+93	998	52.000	2595300	51.875	51.625	52.500
+93	999	53.125	2067200	52.375	51.875	53.250
+93	1000	55.125	4184000	54.000	53.750	55.250
+93	1001	55.750	1211000	55.500	55.125	56.000
+93	1002	54.380	2487900	56.000	54.380	56.130
+93	1003	53.875	1976800	54.500	53.750	55.125
+93	1004	53.130	2385300	54.750	53.130	54.750
+93	1005	53.750	1865800	53.250	53.250	54.125
+94	1006	59.000	1846800	58.130	58.000	59.130
+94	1007	59.500	2570000	59.000	58.500	59.875
+94	1008	58.500	2379800	60.000	58.500	60.000
+94	1009	58.875	1458600	58.500	58.000	59.375
+94	1010	59.250	1734900	58.875	58.500	59.500
+94	1011	58.630	1690600	59.130	58.500	59.380
+94	1012	58.130	1503100	58.750	58.130	58.880
+94	1013	58.750	1804200	58.250	58.250	59.000
+94	1014	58.630	1586000	58.750	58.380	59.500
+94	1015	57.500	1528100	58.500	57.130	58.500
+94	1016	57.125	2255900	57.375	56.375	57.375
+94	1017	56.000	1959500	56.750	55.750	57.000
+94	1018	55.250	2278500	55.875	55.125	55.875
+94	1019	55.250	3176600	55.125	54.750	55.625
+94	1020	58.630	4219300	55.750	55.750	58.750
+94	1021	58.250	10154900	58.880	55.380	59.380
+95	1022	52.880	1565600	53.380	52.750	53.630
+95	1023	53.250	1985500	52.500	52.000	53.380
+95	1024	54.000	1873700	53.375	53.375	54.625
+95	1025	54.500	1727400	54.380	54.000	54.630
+95	1026	54.625	1495700	55.250	54.500	55.250
+95	1027	52.750	2290200	54.880	52.500	55.000
+96	1028	53.380	2097500	53.500	51.380	53.750
+96	1029	52.250	2207700	53.250	51.750	53.500
+96	1030	58.380	8758100	55.000	54.000	58.630
+96	1031	58.750	5660500	58.375	58.000	59.500
+97	1032	58.500	2217200	58.880	58.500	59.250
+97	1033	57.125	2297500	57.875	56.750	58.250
+97	1034	57.500	1908600	57.375	56.875	57.875
+97	1035	58.000	1608500	57.500	56.875	58.375
+97	1036	58.380	1376600	58.130	57.880	58.630
+97	1037	57.750	1500300	58.250	57.500	58.250
+98	1038	55.625	1817200	56.750	55.625	56.875
+98	1039	55.880	3189300	55.750	54.500	56.250
+98	1040	62.380	8807500	59.000	58.000	62.630
+98	1041	61.380	3714100	62.250	61.130	62.380
+99	1042	72.250	2341600	73.625	72.250	74.125
+99	1043	73.125	2040800	72.500	71.875	73.125
+99	1044	73.250	1536400	72.880	72.750	73.750
+99	1045	74.375	2463400	73.500	73.375	74.500
+99	1046	75.380	3933900	74.000	73.750	75.750
+99	1047	75.000	5216900	75.500	73.875	76.125
+99	1048	74.625	3372500	74.625	74.125	75.375
+99	1049	73.000	2615200	74.625	72.875	74.625
+99	1050	73.880	2829600	72.630	72.630	74.380
+100	1051	71.750	1418500	72.750	71.500	73.000
+100	1052	72.130	2742400	72.000	70.250	72.880
+100	1053	73.625	3292000	72.375	72.250	73.875
+100	1054	74.380	1458600	73.630	73.500	74.380
+100	1055	74.625	2562800	74.875	74.500	75.250
+100	1056	74.380	1352100	74.380	74.130	74.880
+100	1057	74.250	1450700	74.250	73.875	74.375
+100	1058	74.250	1496400	74.125	74.125	74.875
+101	1059	87.130	2576400	87.880	86.000	88.250
+101	1060	89.000	5977700	90.500	88.630	90.500
+101	1061	91.625	5886400	89.375	89.125	92.000
+101	1062	93.500	4318900	91.500	91.000	93.880
+101	1063	93.625	3346300	93.125	92.875	93.875
+101	1064	95.250	3694500	93.125	92.875	95.625
+102	1065	89.375	3394000	90.125	88.375	90.750
+102	1066	89.130	3308400	89.630	88.630	90.250
+102	1067	90.250	2995700	90.500	89.750	90.750
+102	1068	91.750	2905200	91.380	90.880	92.250
+102	1069	93.000	2562500	91.875	91.875	93.375
+102	1070	93.000	2192900	93.630	93.000	94.000
+103	1071	109.000	3285600	106.500	105.625	109.500
+103	1072	108.625	1257200	109.125	108.500	109.750
+103	1073	108.875	1430800	109.125	108.625	109.375
+103	1074	109.630	1795300	108.880	108.880	110.000
+103	1075	109.875	1966800	110.000	109.625	110.375
+103	1076	109.125	1971400	109.875	108.875	110.375
+103	1077	109.250	2327700	109.500	108.380	109.630
+103	1078	110.750	1748300	109.250	109.250	110.875
+103	1079	112.000	2701000	111.000	110.250	112.125
+103	1080	112.375	2946800	112.125	110.750	112.750
+103	1081	113.625	3029700	112.375	112.250	114.625
+103	1082	111.375	3698100	113.875	111.125	114.000
+103	1083	106.750	4700700	111.380	106.500	111.380
+103	1084	107.630	5514300	107.000	105.630	108.250
+103	1085	106.625	3239000	108.125	106.500	108.500
+104	1086	91.880	3744400	89.130	89.130	91.880
+104	1087	89.375	3605800	92.000	89.125	92.000
+104	1088	90.875	2674000	89.875	89.750	91.125
+104	1089	91.250	1877400	90.880	90.880	91.880
+104	1090	91.750	945400	92.000	91.625	92.125
+104	1091	92.000	1572000	91.750	90.750	92.000
+105	1092	87.250	4624600	88.130	86.000	88.500
+105	1093	86.375	2664400	87.250	86.125	87.250
+105	1094	83.125	3742800	86.500	83.125	86.500
+105	1095	87.000	4613000	84.125	83.500	87.750
+105	1096	87.625	4821100	86.750	85.750	89.250
+105	1097	96.250	9856900	90.000	89.625	96.250
+105	1098	102.000	13145900	96.625	96.500	104.125
+105	1099	102.250	5293000	101.500	101.375	103.875
+105	1100	103.000	4959100	100.750	100.750	104.500
+105	1101	107.000	7989000	103.130	103.130	109.000
+105	1102	104.125	4721800	107.625	104.125	108.000
+106	1103	113.625	3467900	114.000	113.500	114.875
+106	1104	114.250	3237400	113.880	113.750	115.000
+106	1105	117.625	5835300	114.375	114.250	118.500
+106	1106	118.000	4985900	118.000	117.500	118.880
+106	1107	119.125	5322200	116.750	116.750	120.875
+106	1108	120.250	4097300	119.000	118.630	120.630
+106	1109	124.125	5355800	121.750	121.250	125.625
+106	1110	125.630	4126200	125.000	124.000	126.130
+106	1111	124.880	2882500	123.880	123.880	125.500
+107	1112	108.750	4722900	114.875	108.375	115.125
+107	1113	110.750	4558900	108.375	107.625	112.000
+107	1114	111.250	3619800	111.130	110.250	113.750
+107	1115	109.750	3046600	111.875	109.500	112.375
+107	1116	111.250	2931600	110.380	110.380	113.750
+107	1117	110.380	2379500	112.130	110.000	112.750
+107	1118	117.625	5407700	111.000	110.500	118.125
+107	1119	119.750	4149900	117.500	116.130	120.500
+108	1120	91.750	3683600	89.630	89.130	92.630
+108	1121	103.630	9014900	96.000	95.630	103.750
+108	1122	103.875	3990400	103.125	101.625	104.125
+108	1123	104.750	3338200	103.500	103.380	105.500
+108	1124	107.380	3992800	105.250	103.750	108.000
+108	1125	107.500	3505300	107.125	107.000	108.875
+108	1126	107.250	3120600	108.000	107.250	109.000
+108	1127	108.875	2265800	108.375	107.750	109.375
+108	1128	109.250	2151100	108.000	108.000	109.375
+109	1129	124.750	1312200	124.750	124.000	125.500
+109	1130	124.500	1864400	124.875	124.125	127.125
+109	1131	123.750	2446500	124.000	123.130	125.250
+109	1132	125.880	2013200	124.250	124.000	125.880
+109	1133	125.000	1845100	125.750	124.625	126.250
+109	1134	126.625	2302300	125.875	125.625	127.125
+109	1135	127.875	1835400	126.875	126.875	128.000
+109	1136	128.380	3148000	128.000	127.630	129.500
+109	1137	126.250	2402400	129.000	126.130	129.380
+109	1138	127.750	2248500	125.500	125.500	128.000
+109	1139	129.875	2858300	128.500	128.250	130.125
+109	1140	130.000	1400900	129.880	129.130	130.000
+109	1141	129.500	2863900	130.750	128.630	131.250
+109	1142	127.625	1875700	129.500	127.375	129.500
+109	1143	125.625	2135400	128.750	125.250	129.125
+109	1144	129.375	3473700	125.500	124.000	129.500
+110	1145	133.375	2886400	130.625	130.250	133.875
+110	1146	133.750	3481600	133.125	132.750	135.125
+110	1147	134.375	1569400	133.375	132.875	134.500
+110	1148	134.500	1429200	134.375	134.000	134.875
+110	1149	133.250	2724100	135.000	133.000	135.750
+110	1150	134.750	2392300	133.500	133.250	135.875
+110	1151	136.880	2774500	134.630	134.630	137.630
+110	1152	145.000	7803800	137.630	137.380	145.500
+110	1153	146.750	5777100	144.750	143.000	150.000
+111	1154	157.625	2833000	158.125	156.125	158.500
+111	1155	158.000	5515400	157.500	157.000	160.750
+111	1156	158.125	2799700	158.250	157.750	160.250
+111	1157	159.375	931300	158.125	158.125	159.625
+111	1158	163.000	3316000	159.630	159.630	163.500
+111	1159	162.625	4677800	163.625	162.625	166.000
+111	1160	162.000	4437400	162.875	159.125	164.000
+111	1161	158.500	3036300	161.375	158.375	162.625
+111	1162	155.625	6051900	151.000	151.000	158.000
+111	1163	160.000	2769900	156.630	156.250	160.130
+112	1164	141.250	3583000	143.130	140.250	143.500
+112	1165	137.625	6308500	140.750	137.125	140.875
+112	1166	143.875	4282700	137.625	0.000	144.000
+112	1167	146.500	4198400	144.380	144.130	148.000
+112	1168	146.875	3670400	147.000	144.000	147.500
+112	1169	143.000	1772200	146.880	143.000	146.880
+113	1170	136.500	2634400	134.500	133.250	136.880
+113	1171	137.880	3931900	138.630	134.380	139.000
+113	1172	137.500	1845300	137.380	135.750	138.380
+113	1173	137.750	2655100	137.375	136.750	139.750
+113	1174	139.750	2343200	138.000	137.750	140.130
+113	1175	137.380	2356700	139.630	136.630	141.250
+113	1176	140.000	3022200	137.250	134.750	140.130
+113	1177	142.380	3917100	140.000	139.000	143.750
+113	1178	153.625	10659100	150.500	149.500	157.000
+113	1179	150.750	3005000	152.250	150.625	152.875
+114	1180	98.625	2949600	102.437	98.625	104.125
+114	1181	99.130	2060000	100.130	99.000	100.810
+114	1182	101.690	1069100	99.380	99.250	101.750
+114	1183	102.750	2307300	102.875	102.750	103.687
+114	1184	103.130	3558000	103.690	102.940	105.500
+114	1185	104.625	2736500	104.000	103.187	105.187
+114	1186	105.625	2635000	104.500	104.062	105.750
+114	1187	106.437	5017000	105.562	105.000	106.812
+114	1188	105.250	3555600	105.375	105.062	107.187
+114	1189	104.250	4308200	105.000	102.625	105.000
+114	1190	104.190	4059400	103.500	102.940	105.500
+114	1191	100.062	7182800	104.312	99.375	104.875
+114	1192	100.130	6410700	98.250	97.500	101.130
+114	1193	102.130	3414500	100.560	100.310	102.440
+114	1194	102.625	4275400	103.500	101.250	104.000
+114	1195	103.437	6321700	103.187	102.375	104.937
+114	1196	105.000	4189100	104.875	104.500	105.187
+114	1197	108.380	4970600	105.190	104.690	108.380
+114	1198	100.125	13907800	100.250	99.625	102.000
+114	1199	99.380	6583500	99.810	98.500	100.630
+114	1200	99.187	3911500	99.812	98.937	100.000
+114	1201	98.125	2807900	99.250	98.000	99.937
+114	1202	96.500	7019700	96.750	95.630	97.880
+114	1203	97.000	5192400	97.130	96.500	98.000
+114	1204	98.187	4413600	97.000	97.000	99.312
+114	1205	98.750	2900100	98.312	98.000	99.562
+114	1206	100.562	3437700	100.000	100.000	101.250
+114	1207	99.380	3585600	100.130	98.690	100.250
+115	1208	98.125	5205100	99.125	98.125	99.812
+115	1209	96.125	6464200	97.125	95.875	98.250
+115	1210	97.500	4167100	97.380	96.690	98.190
+115	1211	99.130	3403400	97.880	97.440	99.880
+115	1212	100.250	2951700	99.187	98.187	100.625
+115	1213	99.630	2925300	100.880	99.000	101.130
+115	1214	101.250	3420700	99.690	99.250	101.380
+115	1215	100.880	3585100	101.310	99.560	101.810
+115	1216	101.560	3594700	100.630	100.560	102.310
+115	1217	102.875	2585000	101.687	101.437	103.000
+115	1218	102.062	4107800	103.000	101.437	103.250
+116	1219	108.812	4240700	109.000	107.625	109.125
+116	1220	106.130	4884000	108.750	106.000	109.250
+116	1221	108.130	4276900	106.250	106.190	109.500
+116	1222	111.750	4039200	110.000	109.940	112.310
+116	1223	112.000	3030000	111.880	110.060	112.130
+116	1224	112.440	3187800	112.500	112.000	114.690
+116	1225	113.000	1636400	112.380	112.310	113.440
+116	1226	114.125	2184600	114.125	113.562	114.812
+116	1227	114.810	4356000	114.130	113.500	116.310
+116	1228	116.750	3166400	116.000	115.500	116.937
+116	1229	115.190	3219800	116.560	114.500	116.630
+117	1230	115.000	2258200	113.750	113.630	115.560
+117	1231	117.062	3189500	114.750	114.625	118.500
+117	1232	118.500	3070100	118.250	117.375	119.250
+117	1233	119.380	2339800	118.500	118.250	120.000
+117	1234	119.625	2268600	119.750	119.000	120.625
+117	1235	117.000	2557800	119.250	117.000	120.000
+117	1236	118.380	2180100	117.130	116.000	118.940
+117	1237	120.187	3168400	118.562	118.500	120.375
+117	1238	122.000	4489200	121.000	120.312	123.000
+117	1239	128.130	12308600	128.250	127.750	131.000
+117	1240	127.440	6049900	126.000	124.500	128.440
+117	1241	123.880	4153100	127.190	123.690	129.000
+117	1242	124.250	2874600	125.875	122.500	125.937
+118	1243	123.500	4994700	125.750	123.375	127.437
+118	1244	122.500	5579800	122.130	118.190	122.500
+118	1245	126.562	4965100	121.375	120.625	126.562
+118	1246	129.130	4557100	128.000	127.630	129.940
+118	1247	130.000	3246800	129.000	128.500	130.687
+118	1248	130.500	3319400	130.000	128.190	130.940
+119	1249	133.625	3820400	134.500	132.187	136.187
+119	1250	131.437	3544900	133.750	130.000	134.125
+119	1251	128.500	4149700	130.750	127.630	131.500
+119	1252	125.375	6268700	125.062	123.375	126.437
+119	1253	124.810	7302600	124.130	118.940	125.250
+119	1254	120.250	6721000	123.750	117.310	123.750
+119	1255	119.250	7183700	124.000	118.750	124.000
+119	1256	120.750	6582400	119.500	118.125	122.875
+119	1257	123.500	9156300	120.750	116.812	123.500
+119	1258	127.312	5841600	124.437	122.500	127.937
+119	1259	130.875	5402500	130.125	129.562	132.875
+119	1260	128.190	4696800	130.500	126.560	130.500
+120	1261	147.380	3456500	148.500	147.190	149.940
+120	1262	147.880	4096300	148.880	145.750	149.750
+120	1263	149.000	3293000	147.690	146.810	149.560
+120	1264	149.940	3569000	148.880	147.500	150.940
+120	1265	151.380	3788700	150.750	149.940	152.190
+120	1266	156.000	4371500	150.880	150.690	156.750
+120	1267	157.125	5109100	157.000	156.500	158.875
+120	1268	157.880	4333900	156.690	156.630	159.880
+120	1269	157.437	2997600	157.000	155.750	157.500
+121	1270	165.000	3885800	163.500	162.500	165.810
+121	1271	164.375	2826400	166.875	164.312	166.875
+121	1272	166.062	4381600	165.437	165.062	169.625
+121	1273	171.560	4908400	166.630	166.630	172.190
+121	1274	176.375	3739300	171.562	171.562	178.937
+121	1275	182.250	4352300	177.500	175.250	183.000
+121	1276	185.000	3535000	182.690	181.130	185.380
+121	1277	187.937	1524900	184.750	184.062	187.937
+121	1278	189.250	2634600	186.500	186.000	189.937
+121	1279	187.125	1881100	188.625	187.000	188.937
+121	1280	186.750	2406600	186.875	186.312	188.625
+121	1281	184.375	1932400	186.750	183.500	187.187
+121	1282	183.000	4074800	185.000	181.500	186.500
+121	1283	189.630	4953800	183.000	182.810	189.880
+122	1284	177.937	4600100	172.250	171.062	177.937
+122	1285	176.940	4554600	178.000	175.750	180.000
+122	1286	173.750	4318700	177.250	173.130	179.690
+122	1287	173.625	5234800	172.000	169.312	173.937
+122	1288	169.750	7399000	171.000	166.500	171.880
+122	1289	168.375	4795400	169.500	165.125	169.500
+122	1290	167.750	4371200	170.500	166.880	170.940
+122	1291	166.750	3871500	168.500	165.062	168.937
+122	1292	171.000	10750500	173.500	170.125	177.000
+122	1293	178.380	5803200	173.880	173.750	178.940
+122	1294	178.875	3779900	178.625	177.062	180.000
+122	1295	182.190	5009500	179.880	178.560	184.880
+122	1296	181.500	3504900	181.630	180.250	182.880
+123	1297	182.000	3951400	179.187	177.125	182.312
+123	1298	181.000	3303000	182.000	180.190	183.500
+123	1299	178.060	3452700	179.500	178.000	180.000
+123	1300	177.630	7252500	177.500	172.940	177.750
+123	1301	168.562	9748400	176.625	168.000	178.000
+123	1302	167.000	5948300	168.687	166.625	170.500
+123	1303	165.380	5007500	167.000	164.000	167.630
+123	1304	169.500	5910500	168.000	166.500	170.500
+123	1305	171.312	4914400	172.000	169.500	173.250
+123	1306	172.380	4133200	171.060	170.560	175.880
+123	1307	178.562	3733400	177.000	176.875	181.250
+123	1308	177.250	2917600	180.000	176.500	181.500
+124	1309	187.000	4265500	186.000	182.312	188.062
+124	1310	186.310	3055900	185.630	184.000	188.060
+124	1311	183.440	4489900	183.500	181.060	183.630
+124	1312	180.000	3423200	184.000	179.940	184.000
+124	1313	179.380	3630500	182.750	177.630	183.880
+124	1314	177.750	4165200	179.500	173.500	179.500
+124	1315	170.375	5964400	177.500	170.125	177.500
+124	1316	166.750	7691800	170.880	164.060	172.310
+124	1317	169.750	7358000	167.000	163.000	169.750
+124	1318	171.875	6580800	169.750	165.125	171.875
+124	1319	194.500	19584900	197.250	192.687	198.750
+124	1320	199.750	10375500	193.380	192.940	206.560
+124	1321	209.880	9333000	204.750	202.310	210.750
+124	1322	212.000	10464600	212.000	208.500	215.250
+124	1323	205.000	5670900	209.250	203.500	210.500
+125	1324	209.250	4169800	211.880	208.000	214.000
+125	1325	217.250	4993400	211.250	211.062	217.500
+125	1326	218.630	4774200	217.250	217.060	221.880
+125	1327	221.000	4186400	220.500	217.500	221.875
+125	1328	225.500	6033400	221.125	215.500	228.437
+125	1329	246.000	12165700	234.000	234.000	246.000
+125	1330	239.250	9036900	241.500	236.000	243.500
+125	1331	237.500	5780200	236.000	232.000	239.625
+125	1332	238.500	4316900	238.250	237.000	240.625
+125	1333	235.875	3895800	238.250	233.062	238.750
+125	1334	232.875	3220900	235.625	232.500	236.375
+125	1335	230.380	3452300	232.750	229.190	233.380
+125	1336	223.750	4299100	230.310	221.750	230.690
+125	1337	221.187	4702500	222.500	221.000	226.000
+125	1338	236.250	8314000	223.000	221.440	236.630
+125	1339	116.000	10552600	116.687	112.625	116.875
+125	1340	116.000	6379500	116.000	114.187	116.750
+126	1341	124.440	6043500	122.750	122.310	124.880
+126	1342	122.000	8442100	123.375	121.000	123.625
+126	1343	122.380	6463000	122.000	120.880	124.060
+126	1344	122.940	6245300	122.630	122.060	125.880
+126	1345	124.000	4354600	125.000	122.750	125.190
+126	1346	123.187	4773000	124.250	122.687	125.625
+127	1347	98.000	18720400	94.000	93.562	98.250
+127	1348	104.750	19673300	99.000	98.380	105.000
+127	1349	107.880	13681500	105.500	104.440	108.440
+127	1350	106.125	10665000	108.812	106.000	109.875
+127	1351	104.060	9710300	105.000	101.810	105.000
+127	1352	105.000	3108400	105.937	104.125	106.000
+127	1353	104.187	5677700	104.937	103.375	104.937
+127	1354	103.060	5719900	103.690	102.130	104.500
+127	1355	103.750	5336000	102.562	102.250	104.437
+127	1356	105.560	6208600	103.440	103.380	106.310
+127	1357	112.000	14679900	109.812	107.937	112.875
+127	1358	116.000	9924400	113.000	112.500	116.500
+128	1359	109.190	7117300	108.000	105.750	109.690
+128	1360	109.125	7680700	110.687	108.625	111.062
+128	1361	108.060	4834900	109.060	107.750	110.440
+128	1362	110.125	4773500	108.500	108.062	110.125
+128	1363	108.375	4005500	109.875	108.250	110.312
+128	1364	108.625	4528700	109.125	108.187	110.437
+128	1365	109.750	3740700	109.687	108.125	110.000
+128	1366	109.250	4081300	109.560	109.060	110.750
+128	1367	109.060	2683100	110.440	108.750	110.500
+128	1368	108.750	3435100	109.690	108.560	110.500
+128	1369	107.875	2870300	109.187	106.625	109.500
+128	1370	115.625	10346800	112.437	111.875	115.812
+128	1371	112.060	8227800	114.000	110.880	114.500
+129	1372	107.000	5665900	107.500	106.130	108.250
+129	1373	109.000	6622500	109.000	107.880	109.810
+129	1374	110.000	8856400	108.250	107.687	111.687
+129	1375	112.750	6563500	110.000	109.940	113.380
+129	1376	113.500	6771400	112.810	111.940	114.690
+129	1377	114.250	6137800	114.500	113.875	115.375
+129	1378	115.250	6492400	115.875	111.625	115.875
+129	1379	121.500	12124300	115.375	115.062	122.250
+129	1380	126.880	11862900	125.000	124.630	128.250
+129	1381	122.500	8163000	125.750	122.060	127.000
+130	1382	102.940	6402800	104.130	102.630	104.810
+130	1383	103.500	7428100	102.380	101.000	104.380
+130	1384	117.000	22927900	112.000	111.750	117.812
+130	1385	114.440	8764700	117.000	113.750	117.000
+131	1386	90.130	7705300	90.190	89.810	94.440
+131	1387	86.000	12579500	85.880	84.810	88.000
+131	1388	81.560	12724900	85.500	80.060	87.940
+131	1389	89.000	9514000	84.500	84.500	89.375
+131	1390	84.810	6052300	89.000	84.250	89.560
+132	1391	92.750	5671800	93.750	91.810	94.000
+132	1392	96.687	9712900	95.375	94.312	97.750
+132	1393	108.312	25243300	104.375	103.500	110.000
+132	1394	111.250	14760400	107.500	107.250	113.937
+133	1395	106.470	6793100	107.170	105.370	107.340
+133	1396	99.290	16688100	105.000	97.905	105.010
+133	1397	95.490	14494800	97.900	95.020	98.400
+133	1398	98.390	12127900	95.100	94.200	98.440
+133	1399	94.960	14246600	95.400	93.340	98.900
+134	1400	99.700	9957300	97.000	97.000	99.880
+134	1401	106.500	19849500	103.000	102.300	110.000
+134	1402	114.470	25822400	112.000	110.450	115.900
+134	1403	114.830	12759100	114.350	113.750	116.400
+135	1404	119.600	7726900	117.500	117.500	119.600
+135	1405	117.800	5629100	119.520	117.760	119.900
+135	1406	115.270	5818000	117.350	115.080	117.590
+135	1407	112.650	9598700	114.000	112.000	114.100
+135	1408	111.800	6757600	112.650	111.640	113.860
+135	1409	112.890	5138900	112.500	111.900	113.600
+135	1410	113.640	3812200	113.250	112.850	114.750
+135	1411	116.970	6154400	113.640	113.560	117.600
+135	1412	117.500	9625700	117.600	116.500	119.000
+135	1413	117.250	6423700	117.000	117.000	118.250
+135	1414	116.100	3099100	117.500	115.600	117.500
+135	1415	117.360	6542700	116.300	116.100	118.140
+136	1416	104.720	9312600	106.500	103.650	106.950
+136	1417	101.960	10266800	104.730	101.600	105.400
+136	1418	103.850	8894800	101.600	101.560	104.130
+136	1419	107.250	8767200	105.900	105.620	107.800
+136	1420	108.530	7841800	106.150	106.110	109.300
+136	1421	107.820	5330100	108.530	107.280	109.380
+137	1422	108.800	5675100	108.500	107.210	109.470
+137	1423	108.180	3704100	107.800	107.100	110.090
+137	1424	106.510	4176800	107.250	105.790	107.370
+137	1425	106.250	4028300	105.800	105.260	107.370
+137	1426	104.190	6398000	105.000	104.100	106.100
+137	1427	104.080	6763200	103.700	102.700	104.540
+137	1428	104.950	5254800	103.850	102.810	104.950
+137	1429	105.860	6024400	105.000	104.650	106.640
+137	1430	106.200	5730200	106.500	105.400	106.850
+137	1431	105.010	4713100	106.350	105.010	106.590
+138	1432	91.300	13783100	94.450	90.300	94.450
+138	1433	90.000	13172200	91.300	87.490	91.500
+138	1434	91.720	11077200	90.200	89.900	92.710
+138	1435	92.710	8676200	91.720	91.340	93.480
+138	1436	93.770	7221200	92.300	92.100	93.900
+138	1437	96.950	11118800	92.900	92.400	97.620
+138	1438	97.310	9785100	97.150	96.800	98.880
+138	1439	98.020	9733000	96.650	95.370	98.450
+138	1440	98.500	7093400	98.030	96.750	99.000
+138	1441	97.140	7525300	98.500	96.760	98.500
+139	1442	123.200	7950400	121.120	120.060	123.210
+139	1443	120.250	8061000	121.550	120.120	122.300
+139	1444	121.100	7036000	120.800	119.410	121.480
+139	1445	121.340	6225500	120.150	120.150	122.140
+139	1446	122.200	5596200	121.510	121.350	122.970
+139	1447	123.890	8054200	121.900	121.010	124.700
+140	1448	75.490	7953000	77.850	75.200	78.250
+140	1449	74.650	13296100	75.400	73.250	75.950
+140	1450	75.600	8909700	75.450	75.300	77.400
+140	1451	76.170	8592000	74.200	73.250	76.770
+140	1452	77.140	7775800	76.180	75.160	77.750
+140	1453	75.940	7240900	75.600	75.470	76.900
+141	1454	72.000	10945600	71.350	71.250	73.620
+141	1455	67.600	12056000	72.010	67.160	72.410
+141	1456	68.580	12012900	67.850	67.700	69.400
+141	1457	70.510	8469900	67.950	67.870	70.710
+141	1458	73.500	5243000	72.450	72.000	73.900
+141	1459	71.300	9488100	73.000	71.030	73.480
+141	1460	69.670	11165100	71.400	69.450	72.250
+141	1461	68.760	10073400	70.600	68.530	71.250
+141	1462	69.410	11034600	68.000	67.550	69.750
+142	1463	69.350	10989800	68.900	67.300	70.000
+142	1464	66.400	11576400	68.750	65.760	69.470
+142	1465	71.180	11918600	70.370	68.000	71.400
+142	1466	71.790	10136400	70.500	70.020	72.700
+142	1467	70.400	12708100	70.750	69.110	71.600
+142	1468	68.250	8832000	70.400	67.220	70.700
+142	1469	67.880	6915000	68.210	67.123	68.230
+142	1470	65.990	7126900	67.940	65.850	68.350
+142	1471	67.900	8370800	67.000	66.610	69.420
+142	1472	69.170	8036800	69.000	66.800	69.900
+142	1473	71.610	8432500	69.200	68.350	72.050
+142	1474	71.830	6940900	70.610	70.400	74.300
+142	1475	71.770	5263300	71.050	70.610	72.640
+143	1476	55.070	12155800	56.050	54.810	56.700
+143	1477	57.580	12672600	54.650	54.010	58.480
+143	1478	63.920	17852500	62.000	61.500	63.920
+143	1479	63.420	7925300	61.540	61.540	63.800
+143	1480	68.480	14600200	67.750	66.580	68.480
+143	1481	64.900	15828900	66.600	64.240	67.005
+143	1482	72.200	21344500	72.800	71.230	73.000
+143	1483	74.250	12532700	71.730	70.260	74.250
+144	1484	72.100	10570000	75.500	71.750	75.550
+144	1485	74.560	7538800	72.500	72.160	74.620
+144	1486	76.560	12544800	75.150	74.560	77.500
+144	1487	76.740	11648500	76.560	74.200	77.050
+144	1488	78.670	12865800	77.000	76.020	79.400
+144	1489	78.940	9844300	78.670	78.500	79.790
+144	1490	80.400	9395200	78.900	78.320	80.500
+144	1491	82.500	12005100	82.000	81.700	83.810
+144	1492	81.680	10466700	81.750	80.470	82.010
+145	1493	80.260	5922700	79.750	79.440	81.507
+145	1494	79.760	2420200	80.200	79.760	81.000
+145	1495	78.500	5784300	79.810	78.260	80.700
+145	1496	77.360	6334400	78.500	76.610	79.180
+145	1497	76.250	8066600	77.000	75.600	77.430
+145	1498	77.500	7871400	77.100	77.100	78.460
+145	1499	80.570	7864500	78.800	78.190	80.570
+145	1500	81.650	5963400	80.700	80.210	81.650
+145	1501	83.590	7923300	81.900	81.810	84.590
+145	1502	86.000	11907000	83.950	83.750	86.180
+145	1503	84.190	9509700	85.550	84.070	85.690
+146	1504	76.500	5617700	77.500	76.500	78.090
+146	1505	75.860	11218600	76.100	74.310	76.350
+146	1506	77.450	8300100	76.400	75.350	77.450
+146	1507	79.330	7793000	78.020	77.910	79.510
+146	1508	79.510	5441300	79.080	78.826	79.700
+146	1509	79.040	6295600	79.570	78.710	80.050
+147	1510	79.850	7024700	81.130	79.800	81.330
+147	1511	79.750	7472200	79.700	78.730	80.680
+147	1512	80.690	5298500	79.690	79.380	80.820
+147	1513	80.880	4615600	81.050	80.350	81.270
+147	1514	81.020	4262200	80.870	80.280	81.500
+147	1515	81.510	4634200	81.450	80.650	81.540
+148	1516	89.700	8842500	91.450	89.010	91.510
+148	1517	90.310	5529700	89.900	89.750	90.460
+148	1518	94.020	19813600	95.070	93.550	95.650
+148	1519	95.320	9302100	95.000	94.710	95.350
+148	1520	97.100	9285300	96.000	95.730	97.440
+148	1521	97.700	6952700	97.230	96.640	98.040
+148	1522	97.510	4371600	97.840	97.320	98.160
+149	1523	99.300	3611700	100.060	99.300	100.300
+149	1524	99.710	3562400	99.100	99.080	100.090
+149	1525	99.370	3930500	99.990	99.320	100.000
+149	1526	98.420	4830500	99.310	98.150	99.770
+149	1527	97.800	5447700	98.420	97.520	99.230
+149	1528	97.310	5690200	98.600	97.190	98.600
+149	1529	95.960	6629600	97.400	95.460	97.510
+149	1530	96.790	7012100	95.200	95.200	97.460
+149	1531	96.540	3362500	96.500	96.230	97.090
+150	1532	96.840	4807000	96.570	95.600	96.890
+150	1533	96.390	3548500	96.580	96.130	96.920
+150	1534	96.450	3971800	95.950	95.560	96.980
+150	1535	94.590	5084200	96.490	94.590	96.880
+150	1536	94.530	6391400	94.300	93.770	95.280
+150	1537	93.060	6536400	94.380	92.680	94.740
+150	1538	91.210	8560800	92.000	91.150	92.980
+150	1539	93.300	6290600	92.000	91.680	93.380
+151	1540	85.350	5677300	85.300	85.050	85.940
+151	1541	83.650	7530100	85.000	83.580	85.010
+151	1542	83.890	6595200	84.400	83.510	84.500
+151	1543	84.950	6009200	84.000	83.420	85.250
+151	1544	85.250	6001500	85.900	85.200	86.090
+151	1545	84.130	6298700	84.840	83.780	85.240
+152	1546	83.690	5268500	83.700	83.100	83.980
+152	1547	82.210	7134900	83.050	81.900	83.050
+152	1548	83.910	5916900	82.540	82.510	83.940
+152	1549	84.020	4361500	83.700	83.510	84.560
+152	1550	84.040	3559500	84.100	83.660	84.540
+152	1551	85.130	4833700	83.600	83.570	85.130
+153	1552	84.160	4650300	84.100	83.980	84.440
+153	1553	84.480	3874200	84.350	83.880	84.650
+153	1554	84.980	4204600	84.480	84.150	84.980
+153	1555	85.740	5198000	85.140	85.010	85.980
+153	1556	86.720	4542700	85.950	85.880	86.980
+153	1557	87.160	5001400	87.000	86.720	88.100
+153	1558	87.320	5150800	87.950	87.130	88.030
+153	1559	88.040	3984400	87.140	87.100	88.100
+153	1560	87.420	3076900	88.040	87.400	88.100
+153	1561	86.710	4090000	87.430	86.510	87.910
+153	1562	86.630	3016300	86.770	86.270	87.200
+153	1563	86.000	4626600	86.020	85.580	86.200
+153	1564	84.980	6665200	86.260	84.430	86.480
+153	1565	84.780	4233700	84.750	84.300	84.980
+153	1566	84.850	5928500	84.780	84.600	85.250
+153	1567	85.920	7275600	84.300	84.290	86.150
+153	1568	89.370	13824500	88.200	88.000	89.730
+153	1569	88.820	7079800	88.450	88.290	89.190
+153	1570	88.100	6272100	88.400	87.660	88.760
+153	1571	87.390	5998900	88.220	87.290	88.450
+153	1572	88.430	5775000	87.360	87.310	88.900
+153	1573	89.000	7337300	88.330	88.250	89.570
+153	1574	90.000	6035100	88.580	88.500	90.270
+153	1575	89.500	4226500	89.800	89.430	90.240
+153	1576	89.750	4518500	89.400	88.950	89.900
+153	1577	90.110	5160900	89.330	89.230	90.600
+153	1578	90.470	5391100	89.550	89.500	91.220
+153	1579	91.200	6553300	91.250	90.970	91.900
+153	1580	92.380	6952100	91.050	90.820	92.700
+153	1581	93.280	6708500	92.400	92.400	93.520
+153	1582	93.370	4907300	92.500	92.500	93.700
+153	1583	93.370	4513100	93.000	93.000	93.950
+154	1584	94.130	3541200	94.280	94.130	94.640
+154	1585	92.700	5664200	94.140	92.540	94.830
+154	1586	92.760	4838700	92.950	92.300	93.100
+154	1587	93.300	4233000	92.700	92.500	93.970
+154	1588	93.570	2868800	93.160	93.160	94.020
+154	1589	94.330	4299200	93.500	93.480	94.670
+155	1590	93.300	3821900	92.640	92.590	93.430
+155	1591	92.920	4541200	92.750	92.750	93.730
+155	1592	92.410	4037900	93.150	92.200	93.210
+155	1593	92.370	4754000	92.940	92.360	93.180
+155	1594	91.600	8051000	92.350	91.590	92.510
+155	1595	92.130	5087800	91.700	91.700	92.560
+155	1596	92.350	4513900	92.020	92.010	93.000
+155	1597	92.410	3424800	92.350	92.090	92.800
+155	1598	91.510	4494300	92.250	91.200	92.410
+156	1599	91.380	4419100	90.460	90.220	91.410
+156	1600	90.440	5740800	91.490	90.040	91.760
+156	1601	90.320	3737900	90.080	89.773	90.620
+156	1602	89.570	4579300	90.230	89.260	90.330
+156	1603	89.000	7836800	89.050	88.706	89.380
+156	1604	88.440	6376500	89.000	88.100	89.200
+156	1605	87.600	5273400	88.280	87.500	88.460
+156	1606	86.200	8296500	87.350	86.090	87.560
+156	1607	85.750	8450000	85.900	85.170	86.240
+156	1608	84.570	7090800	85.760	84.240	85.972
+156	1609	83.640	10708300	84.630	83.470	85.410
+156	1610	76.700	27932400	79.490	76.330	79.660
+156	1611	76.650	13256100	77.150	76.140	77.750
+156	1612	75.480	9292700	76.980	75.250	77.200
+156	1613	72.010	20389800	75.480	71.850	75.870
+156	1614	74.030	16273600	72.990	72.800	74.100
+156	1615	74.210	10985200	74.050	73.260	74.700
+156	1616	74.610	10223400	75.240	74.050	75.720
+156	1617	75.430	12500200	74.680	74.650	76.980
+156	1618	77.050	11706800	75.690	75.500	77.180
+156	1619	75.910	8638300	77.050	75.650	77.110
+157	1620	83.140	5340900	82.930	82.500	83.780
+157	1621	84.000	4490200	83.140	83.000	84.080
+157	1622	84.190	4712900	83.710	83.560	84.630
+157	1623	84.190	5210600	84.150	83.790	84.690
+157	1624	84.700	4661700	84.200	84.100	84.900
+157	1625	86.080	7788600	84.450	84.440	86.210
+157	1626	86.710	8633600	86.080	85.750	87.600
+157	1627	86.950	13328300	86.330	86.220	87.940
+157	1628	89.820	27876200	90.510	89.730	92.040
+157	1629	89.860	10676400	89.800	89.310	90.980
+157	1630	90.480	7391600	90.040	89.550	90.700
+157	1631	91.560	8862300	90.050	90.010	92.000
+157	1632	91.490	9222500	90.910	90.800	91.950
diff --git a/src/test/regress/expected/rpr.out b/src/test/regress/expected/rpr.out
index 35b90a04492..ecbe835cbb4 100644
--- a/src/test/regress/expected/rpr.out
+++ b/src/test/regress/expected/rpr.out
@@ -1,11 +1,25 @@
 --
 -- Test for row pattern recognition: WINDOW clause integration and
--- real-world scenario tests using stock price data.
+-- scenario tests using synthetic stock data.
 --
 -- Parser/planner tests: rpr_base.sql
 -- NFA engine tests: rpr_nfa.sql
 -- EXPLAIN statistics tests: rpr_explain.sql
 --
+\getenv abs_srcdir PG_ABS_SRCDIR
+-- Synthetic stock data for RPR pattern matching tests
+CREATE TABLE rpr_stock (
+       part_id integer,
+       rn      integer,
+       price   numeric(10,3),
+       volume  bigint,
+       open    numeric(10,3),
+       low     numeric(10,3),
+       high    numeric(10,3)
+);
+\set filename :abs_srcdir '/data/stock.data'
+COPY rpr_stock FROM :'filename';
+ANALYZE rpr_stock;
 CREATE TEMP TABLE stock (
        company TEXT,
        tdate DATE,
@@ -1839,3 +1853,504 @@ WINDOW w AS (
 (6 rows)
 
 DROP TABLE rpr_consec_null;
+-- ============================================================
+-- Stock Scenario Tests (1632 rows, partitioned regions)
+-- ============================================================
+-- Consecutive rising days: find streaks of 7+ days
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (UP{7,})
+        DEFINE UP AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | days 
+----------+--------+------
+       29 |     35 |    7
+       38 |     44 |    7
+       96 |    102 |    7
+      118 |    125 |    8
+      308 |    317 |   10
+      328 |    334 |    7
+      475 |    481 |    7
+      491 |    497 |    7
+      509 |    517 |    9
+      536 |    542 |    7
+      586 |    592 |    7
+      643 |    650 |    8
+      740 |    746 |    7
+      753 |    760 |    8
+      904 |    910 |    7
+      956 |    965 |   10
+      985 |    991 |    7
+     1095 |   1101 |    7
+     1104 |   1110 |    7
+     1181 |   1187 |    7
+     1221 |   1228 |    8
+     1262 |   1268 |    7
+     1272 |   1278 |    7
+     1373 |   1380 |    8
+     1434 |   1440 |    7
+     1485 |   1491 |    7
+     1553 |   1559 |    7
+     1576 |   1582 |    7
+     1624 |   1631 |    8
+(29 rows)
+
+-- V-shape recovery: 4+ days decline followed by 4+ days rise
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{4,} RISE{4,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            RISE AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+       84 |     93 |     301.000 |   309.750 |   10
+      114 |    125 |     394.500 |   418.750 |   12
+      173 |    183 |     260.000 |   282.000 |   11
+      204 |    214 |     179.000 |   171.500 |   11
+      262 |    271 |     277.625 |   275.500 |   10
+      337 |    344 |     308.250 |   308.125 |    8
+      436 |    444 |      56.630 |    60.250 |    9
+      567 |    575 |     126.500 |   127.500 |    9
+      598 |    607 |     112.250 |   116.500 |   10
+      653 |    660 |     129.375 |   128.125 |    8
+      663 |    671 |     125.630 |   130.250 |    9
+      685 |    693 |     120.130 |   124.000 |    9
+      710 |    719 |     141.125 |   144.130 |   10
+      833 |    841 |     106.500 |   111.125 |    9
+      853 |    862 |     105.375 |   107.750 |   10
+      930 |    937 |      92.380 |    96.375 |    8
+     1188 |   1197 |     105.250 |   108.380 |   10
+     1198 |   1206 |     100.125 |   100.562 |    9
+     1250 |   1259 |     131.437 |   130.875 |   10
+     1285 |   1295 |     176.940 |   182.190 |   11
+     1298 |   1307 |     181.000 |   178.562 |   10
+     1310 |   1322 |     186.310 |   212.000 |   13
+     1405 |   1412 |     117.800 |   117.500 |    8
+     1467 |   1474 |      70.400 |    71.830 |    8
+     1494 |   1502 |      79.760 |    86.000 |    9
+     1600 |   1618 |      90.440 |    77.050 |   19
+(26 rows)
+
+-- W-bottom: decline, bounce, re-decline, recovery
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{3,} BOUNCE{3,} DIP{3,} RECOVER{3,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            BOUNCE AS price > PREV(price),
+            DIP AS price < PREV(price),
+            RECOVER AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+      136 |    153 |     444.000 |   434.250 |   18
+      456 |    469 |      64.500 |    65.125 |   14
+      520 |    534 |     115.250 |   115.750 |   15
+      610 |    623 |     107.125 |   109.000 |   14
+      791 |    802 |     113.500 |   118.250 |   12
+      942 |    953 |      91.250 |    89.875 |   12
+     1188 |   1206 |     105.250 |   100.562 |   19
+     1560 |   1574 |      87.420 |    90.000 |   15
+(8 rows)
+
+-- Volume surge streak: 6+ consecutive days of increasing volume
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(volume) OVER w AS start_vol,
+           last_value(volume) OVER w AS end_vol,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT SURGE{5,})
+        DEFINE
+            SURGE AS volume > PREV(volume)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_vol | end_vol  | days 
+----------+--------+-----------+----------+------
+      186 |    191 |     25100 |    35300 |    6
+      291 |    296 |     52200 |   188300 |    6
+      408 |    413 |    163300 |   610300 |    6
+      439 |    444 |    438400 |  1089200 |    6
+      500 |    506 |    373700 |  1114200 |    7
+      551 |    558 |    691100 |  2097500 |    8
+      635 |    640 |    418300 |  1388100 |    6
+      783 |    788 |    769000 |  1564900 |    6
+      824 |    830 |    682200 |  2418700 |    7
+      968 |    974 |    993100 |  2341200 |    7
+     1072 |   1077 |   1257200 |  2327700 |    6
+     1078 |   1084 |   1748300 |  5514300 |    7
+     1093 |   1098 |   2664400 | 13145900 |    6
+     1334 |   1339 |   3220900 | 10552600 |    6
+     1524 |   1530 |   3562400 |  7012100 |    7
+     1533 |   1538 |   3548500 |  8560800 |    6
+     1575 |   1580 |   4226500 |  6952100 |    6
+(17 rows)
+
+-- Volatility squeeze: consecutive narrowing of daily price range
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(high - low) OVER w AS start_range,
+           last_value(high - low) OVER w AS end_range,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT NARROW{5,})
+        DEFINE
+            NARROW AS (high - low) < PREV(high) - PREV(low)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_range | end_range | days 
+----------+--------+-------------+-----------+------
+      128 |    133 |      11.000 |     3.000 |    6
+      170 |    175 |      10.250 |     2.750 |    6
+      194 |    201 |      10.000 |     1.625 |    8
+      283 |    288 |       7.000 |     2.375 |    6
+      320 |    325 |       4.750 |     2.500 |    6
+      578 |    583 |       2.750 |     1.125 |    6
+      725 |    731 |       3.370 |     1.125 |    7
+      775 |    780 |       4.500 |     0.875 |    6
+      913 |    918 |       2.870 |     1.250 |    6
+     1130 |   1135 |       3.000 |     1.125 |    6
+     1348 |   1353 |       6.620 |     1.562 |    6
+(11 rows)
+
+-- Gap up: open significantly higher than previous close (5%+)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS gap_rn,
+           first_value(price) OVER w AS prev_close,
+           last_value(open) OVER w AS gap_open,
+           count(*) OVER w AS cnt
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (PREV_DAY GAP_UP)
+        DEFINE
+            GAP_UP AS open > PREV(price) * 1.05
+    )
+) t WHERE cnt > 0 ORDER BY gap_rn;
+ gap_rn | prev_close | gap_open | cnt 
+--------+------------+----------+-----
+    986 |     48.875 |   52.875 |   2
+    994 |     42.375 |   44.880 |   2
+   1029 |     52.250 |   55.000 |   2
+   1039 |     55.880 |   59.000 |   2
+   1177 |    142.380 |  150.500 |   2
+   1238 |    122.000 |  128.250 |   2
+   1318 |    171.875 |  197.250 |   2
+   1383 |    103.500 |  112.000 |   2
+   1392 |     96.687 |  104.375 |   2
+   1401 |    106.500 |  112.000 |   2
+   1464 |     66.400 |   70.370 |   2
+   1477 |     57.580 |   62.000 |   2
+   1479 |     63.420 |   67.750 |   2
+   1481 |     64.900 |   72.800 |   2
+   1517 |     90.310 |   95.070 |   2
+(15 rows)
+
+-- Price-volume divergence: price rising while volume declining (bearish signal)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT DIVERGE{3,})
+        DEFINE
+            DIVERGE AS price > PREV(price) AND volume < PREV(volume)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+       10 |     13 |     338.500 |   349.750 |    4
+       47 |     50 |     352.000 |   358.000 |    4
+       53 |     56 |     314.000 |   319.000 |    4
+       63 |     66 |     294.500 |   301.750 |    4
+       99 |    102 |     401.000 |   404.000 |    4
+      177 |    180 |     248.500 |   261.750 |    4
+      209 |    212 |     152.000 |   166.500 |    4
+      237 |    240 |     207.500 |   215.500 |    4
+      274 |    277 |     259.500 |   264.500 |    4
+      347 |    350 |     306.750 |   312.000 |    4
+      531 |    534 |     111.875 |   115.750 |    4
+      545 |    548 |     118.000 |   121.000 |    4
+      561 |    564 |     131.130 |   134.250 |    4
+      586 |    589 |     110.250 |   112.000 |    4
+      645 |    648 |     129.630 |   133.250 |    4
+      656 |    660 |     124.500 |   128.125 |    5
+      679 |    682 |     128.000 |   129.750 |    4
+      734 |    737 |     121.380 |   123.750 |    4
+      763 |    766 |     150.750 |   156.500 |    4
+      844 |    847 |     105.500 |   107.880 |    4
+      921 |    924 |      97.250 |   101.250 |    4
+      956 |    959 |      84.875 |    87.500 |    4
+      960 |    963 |      88.250 |    89.500 |    4
+      987 |    990 |      49.000 |    51.500 |    4
+     1023 |   1026 |      53.250 |    54.625 |    4
+     1033 |   1036 |      57.125 |    58.380 |    4
+     1060 |   1063 |      89.000 |    93.625 |    4
+     1066 |   1069 |      89.130 |    93.000 |    4
+     1087 |   1090 |      89.375 |    91.750 |    4
+     1165 |   1168 |     137.625 |   146.875 |    4
+     1202 |   1205 |      96.500 |    98.750 |    4
+     1209 |   1212 |      96.125 |   100.250 |    4
+     1220 |   1223 |     106.130 |   112.000 |    4
+     1231 |   1234 |     117.062 |   119.625 |    4
+     1244 |   1247 |     122.500 |   130.000 |    4
+     1304 |   1307 |     169.500 |   178.562 |    4
+     1342 |   1345 |     122.000 |   124.000 |    4
+     1417 |   1420 |     101.960 |   108.530 |    4
+     1433 |   1436 |      90.000 |    93.770 |    4
+     1437 |   1440 |      96.950 |    98.500 |    4
+     1443 |   1446 |     120.250 |   122.200 |    4
+     1449 |   1452 |      74.650 |    77.140 |    4
+     1455 |   1458 |      67.600 |    73.500 |    4
+     1497 |   1500 |      76.250 |    81.650 |    4
+     1505 |   1508 |      75.860 |    79.510 |    4
+     1511 |   1514 |      79.750 |    81.020 |    4
+     1518 |   1521 |      94.020 |    97.700 |    4
+     1541 |   1544 |      83.650 |    85.250 |    4
+     1547 |   1550 |      82.210 |    84.040 |    4
+     1585 |   1588 |      92.700 |    93.570 |    4
+     1594 |   1597 |      91.600 |    92.410 |    4
+     1613 |   1616 |      72.010 |    74.610 |    4
+(52 rows)
+
+-- Consolidation then breakout: sideways movement followed by sharp rise
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (FLAT{5,} BREAKOUT)
+        DEFINE
+            FLAT AS price BETWEEN PREV(price) * 0.98 AND PREV(price) * 1.02,
+            BREAKOUT AS price > PREV(price) * 1.05
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+       69 |     81 |     296.750 |   314.000 |   13
+      214 |    225 |     171.500 |   164.125 |   12
+      371 |    395 |      71.630 |    71.250 |   25
+      416 |    424 |      54.250 |    53.875 |    9
+      484 |    494 |      75.500 |    79.000 |   11
+      865 |    892 |     112.500 |   115.750 |   28
+     1007 |   1020 |      59.500 |    58.630 |   14
+     1113 |   1118 |     110.750 |   117.625 |    6
+     1146 |   1152 |     133.750 |   145.000 |    7
+     1171 |   1178 |     137.880 |   153.625 |    8
+     1350 |   1357 |     106.125 |   112.000 |    8
+     1360 |   1370 |     109.125 |   115.625 |   11
+(12 rows)
+
+-- Dead cat bounce: decline followed by weak recovery (<1% per day)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{4,} BOUNCE{3,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            BOUNCE AS price > PREV(price) AND price < PREV(price) * 1.01
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+       59 |     66 |     315.000 |   301.750 |    8
+      262 |    271 |     277.625 |   275.500 |   10
+      280 |    287 |     272.000 |   261.250 |    8
+      361 |    368 |      75.375 |    74.130 |    8
+      427 |    433 |      65.130 |    65.500 |    7
+      447 |    453 |      55.500 |    54.625 |    7
+      653 |    659 |     129.375 |   126.625 |    7
+      674 |    682 |     132.375 |   129.750 |    9
+      833 |    839 |     106.500 |   107.125 |    7
+     1423 |   1430 |     108.180 |   106.200 |    8
+     1591 |   1597 |      92.920 |    92.410 |    7
+(11 rows)
+
+-- Uptrend: 7+ consecutive days of higher highs AND higher lows
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (UPTREND{7,})
+        DEFINE
+            UPTREND AS high > PREV(high) AND low > PREV(low)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+      156 |    162 |     317.000 |   329.500 |    7
+      299 |    305 |     266.000 |   273.500 |    7
+      696 |    702 |     144.630 |   152.500 |    7
+      741 |    747 |     155.250 |   166.750 |    7
+      895 |    901 |     119.250 |   126.750 |    7
+     1121 |   1127 |     103.630 |   108.875 |    7
+     1211 |   1217 |      99.130 |   102.875 |    7
+     1271 |   1278 |     164.375 |   189.250 |    8
+     1621 |   1628 |      84.000 |    89.820 |    8
+(9 rows)
+
+-- Panic and snap-back: 3%+ daily drops followed by 2%+ rebound
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (PANIC{2,} SNAP)
+        DEFINE
+            PANIC AS price < PREV(price) * 0.97,
+            SNAP AS price > PREV(price) * 1.02
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+       24 |     26 |     230.625 |   229.875 |    3
+      165 |    167 |     272.000 |   270.750 |    3
+      169 |    171 |     260.000 |   259.500 |    3
+      769 |    772 |     140.125 |   115.000 |    4
+      977 |    979 |      56.130 |    53.000 |    3
+      980 |    982 |      51.375 |    51.750 |    3
+     1387 |   1389 |      86.000 |    89.000 |    3
+     1396 |   1398 |      99.290 |    98.390 |    3
+(8 rows)
+
+-- Volume climax reversal: uptrend, volume spike (1.5x), then decline
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (RALLY{3,} CLIMAX SELLOFF{2,})
+        DEFINE
+            RALLY AS price > PREV(price),
+            CLIMAX AS volume > PREV(volume) * 1.5,
+            SELLOFF AS price < PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+ start_rn | end_rn | start_price | end_price | days 
+----------+--------+-------------+-----------+------
+        2 |      7 |     368.250 |   367.750 |    6
+       16 |     21 |     349.250 |   344.500 |    6
+      105 |    111 |     388.500 |   394.500 |    7
+      228 |    234 |     164.250 |   163.250 |    7
+      243 |    248 |     223.250 |   228.500 |    6
+      251 |    259 |     251.000 |   253.500 |    9
+      352 |    358 |     310.500 |   309.000 |    7
+      398 |    405 |      65.380 |    65.000 |    8
+      466 |    472 |      64.250 |    63.875 |    7
+      586 |    595 |     110.250 |   112.250 |   10
+      626 |    632 |     125.750 |   123.250 |    7
+      700 |    707 |     150.250 |   152.750 |    8
+      714 |    722 |     136.125 |   137.000 |    9
+      740 |    750 |     154.250 |   163.000 |   11
+      805 |    811 |     116.875 |   116.000 |    7
+      814 |    821 |      99.500 |    97.130 |    8
+      850 |    857 |     107.880 |    99.250 |    8
+      922 |    927 |      99.750 |    99.875 |    6
+      934 |    939 |      92.375 |    92.880 |    6
+      998 |   1004 |      52.000 |    53.130 |    7
+     1043 |   1049 |      73.125 |    73.000 |    7
+     1052 |   1057 |      72.130 |    74.250 |    6
+     1138 |   1143 |     127.750 |   125.625 |    6
+     1155 |   1162 |     158.000 |   155.625 |    8
+     1181 |   1191 |      99.130 |   100.062 |   11
+     1192 |   1202 |     100.130 |    96.500 |   11
+     1236 |   1241 |     118.380 |   123.880 |    6
+     1272 |   1282 |     166.062 |   183.000 |   11
+     1325 |   1331 |     217.250 |   237.500 |    7
+     1409 |   1414 |     112.890 |   116.100 |    6
+     1456 |   1461 |      68.580 |    68.760 |    6
+(31 rows)
+
diff --git a/src/test/regress/sql/rpr.sql b/src/test/regress/sql/rpr.sql
index 8dbd3f9036a..b308f8d7cb4 100644
--- a/src/test/regress/sql/rpr.sql
+++ b/src/test/regress/sql/rpr.sql
@@ -1,12 +1,29 @@
 --
 -- Test for row pattern recognition: WINDOW clause integration and
--- real-world scenario tests using stock price data.
+-- scenario tests using synthetic stock data.
 --
 -- Parser/planner tests: rpr_base.sql
 -- NFA engine tests: rpr_nfa.sql
 -- EXPLAIN statistics tests: rpr_explain.sql
 --
 
+\getenv abs_srcdir PG_ABS_SRCDIR
+
+-- Synthetic stock data for RPR pattern matching tests
+CREATE TABLE rpr_stock (
+       part_id integer,
+       rn      integer,
+       price   numeric(10,3),
+       volume  bigint,
+       open    numeric(10,3),
+       low     numeric(10,3),
+       high    numeric(10,3)
+);
+
+\set filename :abs_srcdir '/data/stock.data'
+COPY rpr_stock FROM :'filename';
+ANALYZE rpr_stock;
+
 CREATE TEMP TABLE stock (
        company TEXT,
        tdate DATE,
@@ -908,3 +925,240 @@ WINDOW w AS (
 );
 
 DROP TABLE rpr_consec_null;
+
+-- ============================================================
+-- Stock Scenario Tests (1632 rows, partitioned regions)
+-- ============================================================
+
+-- Consecutive rising days: find streaks of 7+ days
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (UP{7,})
+        DEFINE UP AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- V-shape recovery: 4+ days decline followed by 4+ days rise
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{4,} RISE{4,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            RISE AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- W-bottom: decline, bounce, re-decline, recovery
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{3,} BOUNCE{3,} DIP{3,} RECOVER{3,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            BOUNCE AS price > PREV(price),
+            DIP AS price < PREV(price),
+            RECOVER AS price > PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Volume surge streak: 6+ consecutive days of increasing volume
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(volume) OVER w AS start_vol,
+           last_value(volume) OVER w AS end_vol,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT SURGE{5,})
+        DEFINE
+            SURGE AS volume > PREV(volume)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Volatility squeeze: consecutive narrowing of daily price range
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(high - low) OVER w AS start_range,
+           last_value(high - low) OVER w AS end_range,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT NARROW{5,})
+        DEFINE
+            NARROW AS (high - low) < PREV(high) - PREV(low)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Gap up: open significantly higher than previous close (5%+)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS gap_rn,
+           first_value(price) OVER w AS prev_close,
+           last_value(open) OVER w AS gap_open,
+           count(*) OVER w AS cnt
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (PREV_DAY GAP_UP)
+        DEFINE
+            GAP_UP AS open > PREV(price) * 1.05
+    )
+) t WHERE cnt > 0 ORDER BY gap_rn;
+
+-- Price-volume divergence: price rising while volume declining (bearish signal)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (INIT DIVERGE{3,})
+        DEFINE
+            DIVERGE AS price > PREV(price) AND volume < PREV(volume)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Consolidation then breakout: sideways movement followed by sharp rise
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (FLAT{5,} BREAKOUT)
+        DEFINE
+            FLAT AS price BETWEEN PREV(price) * 0.98 AND PREV(price) * 1.02,
+            BREAKOUT AS price > PREV(price) * 1.05
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Dead cat bounce: decline followed by weak recovery (<1% per day)
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (DECLINE{4,} BOUNCE{3,})
+        DEFINE
+            DECLINE AS price < PREV(price),
+            BOUNCE AS price > PREV(price) AND price < PREV(price) * 1.01
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Uptrend: 7+ consecutive days of higher highs AND higher lows
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (UPTREND{7,})
+        DEFINE
+            UPTREND AS high > PREV(high) AND low > PREV(low)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Panic and snap-back: 3%+ daily drops followed by 2%+ rebound
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (PANIC{2,} SNAP)
+        DEFINE
+            PANIC AS price < PREV(price) * 0.97,
+            SNAP AS price > PREV(price) * 1.02
+    )
+) t WHERE days > 0 ORDER BY start_rn;
+
+-- Volume climax reversal: uptrend, volume spike (1.5x), then decline
+SELECT * FROM (
+    SELECT first_value(rn) OVER w AS start_rn,
+           last_value(rn) OVER w AS end_rn,
+           first_value(price) OVER w AS start_price,
+           last_value(price) OVER w AS end_price,
+           count(*) OVER w AS days
+    FROM rpr_stock
+    WINDOW w AS (
+        PARTITION BY part_id
+        ORDER BY rn
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (RALLY{3,} CLIMAX SELLOFF{2,})
+        DEFINE
+            RALLY AS price > PREV(price),
+            CLIMAX AS volume > PREV(volume) * 1.5,
+            SELLOFF AS price < PREV(price)
+    )
+) t WHERE days > 0 ORDER BY start_rn;
-- 
2.50.1 (Apple Git-155)


From d13cf490ce7585fd99a7690911a75d90a51017bb Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 14:08:47 +0900
Subject: [PATCH 8/8] Fix zero-min reluctant quantifier to produce zero-length
 match


diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 4441b4d9c51..3075c51789c 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -307,24 +307,24 @@ static inline bool nfa_eval_var_match(WindowAggState *winstate,
 static void nfa_match(WindowAggState *winstate, RPRNFAContext *ctx,
 					  bool *varMatched);
 static void nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
-							  RPRNFAState *state, int64 currentPos, bool initialAdvance);
+							  RPRNFAState *state, int64 currentPos);
 static void nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 							  RPRNFAState *state, RPRPatternElement *nextElem,
-							  int64 currentPos, bool initialAdvance);
+							  int64 currentPos);
 static void nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 							  RPRNFAState *state, RPRPatternElement *elem,
-							  int64 currentPos, bool initialAdvance);
+							  int64 currentPos);
 static void nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx,
-						int64 currentPos, bool initialAdvance);
+						int64 currentPos);
 
 /*
  * Not null info bit array consists of 2-bit items
@@ -5008,9 +5008,14 @@ register_result:
  *   handles nested groups like "((A|B)+)+" correctly - exiting the inner
  *   group counts as one iteration of the outer group.
  *
- * - initialAdvance flag: The first advance after context creation must
- *   skip FIN recording. Reaching FIN without evaluating any VAR would
- *   create a zero-length match, which is invalid.
+ * - Zero-length match handling: The initial advance uses currentPos =
+ *   startPos - 1 (before any row is consumed). If FIN is reached via
+ *   epsilon transitions alone, matchEndRow = startPos - 1 < matchStartRow,
+ *   resulting in UNMATCHED. For reluctant min=0 patterns (A*?, A??),
+ *   the skip path reaches FIN first and early termination prunes enter
+ *   paths, yielding an immediate zero-length (unmatched) result. For
+ *   greedy patterns (A*), the enter path adds VAR states first, then
+ *   the skip FIN is recorded but VAR states survive for later matching.
  *
  * Context Absorption Runtime:
  * ---------------------------
@@ -5122,7 +5127,7 @@ nfa_process_row(WindowAggState *winstate, int64 currentPos,
 		Assert(!hasLimitedFrame ||
 			   currentPos < ctx->matchStartRow + frameOffset + 1);
 
-		nfa_advance(winstate, ctx, currentPos, false);
+		nfa_advance(winstate, ctx, currentPos);
 	}
 }
 
@@ -5467,10 +5472,11 @@ nfa_start_context(WindowAggState *winstate, int64 startPos)
 	 * states for VAR elements with min=0. This prepares the context for the
 	 * first row's match phase.
 	 *
-	 * Pass initialAdvance=true to prevent recording zero-length matches when
-	 * optional patterns can skip all VARs to reach FIN immediately.
+	 * Use startPos - 1 as currentPos since no row has been consumed yet. If
+	 * FIN is reached via epsilon transitions, matchEndRow = startPos - 1
+	 * which is less than matchStartRow, resulting in UNMATCHED treatment.
 	 */
-	nfa_advance(winstate, ctx, startPos, true);
+	nfa_advance(winstate, ctx, startPos - 1);
 
 	return ctx;
 }
@@ -5711,7 +5717,7 @@ nfa_finalize_all_contexts(WindowAggState *winstate, int64 lastPos)
 		if (ctx->states != NULL)
 		{
 			nfa_match(winstate, ctx, NULL);
-			nfa_advance(winstate, ctx, lastPos, false);
+			nfa_advance(winstate, ctx, lastPos);
 		}
 	}
 }
@@ -6047,7 +6053,7 @@ nfa_match(WindowAggState *winstate, RPRNFAContext *ctx, bool *varMatched)
 static void
 nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 				  RPRNFAState *state, RPRPatternElement *nextElem,
-				  int64 currentPos, bool initialAdvance)
+				  int64 currentPos)
 {
 	if (RPRElemIsVar(nextElem))
 	{
@@ -6061,11 +6067,11 @@ nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 		nfa_add_state_unique(winstate, ctx, state);
 
 		if (skipState != NULL)
-			nfa_advance_state(winstate, ctx, skipState, currentPos, initialAdvance);
+			nfa_advance_state(winstate, ctx, skipState, currentPos);
 	}
 	else
 	{
-		nfa_advance_state(winstate, ctx, state, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, state, currentPos);
 	}
 }
 
@@ -6077,7 +6083,7 @@ nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6097,7 +6103,7 @@ nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 									state->counts, state->isAbsorbable);
 
 		/* Recursively process this branch before next */
-		nfa_advance_state(winstate, ctx, newState, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, newState, currentPos);
 		altIdx = altElem->jump;
 	}
 
@@ -6115,7 +6121,7 @@ nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 				  RPRNFAState *state, RPRPatternElement *elem,
-				  int64 currentPos, bool initialAdvance)
+				  int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6136,7 +6142,7 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 
 		/* Reluctant: skip first (prefer fewer iterations), enter second */
 		nfa_route_to_elem(winstate, ctx, skipState,
-						  &elements[elem->jump], currentPos, initialAdvance);
+						  &elements[elem->jump], currentPos);
 
 		/*
 		 * If skip path reached FIN, shortest match is found. Skip group entry
@@ -6150,19 +6156,19 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 
 		state->elemIdx = elem->next;
 		nfa_route_to_elem(winstate, ctx, state,
-						  &elements[state->elemIdx], currentPos, initialAdvance);
+						  &elements[state->elemIdx], currentPos);
 	}
 	else
 	{
 		/* Greedy: enter first, skip second */
 		state->elemIdx = elem->next;
 		nfa_route_to_elem(winstate, ctx, state,
-						  &elements[state->elemIdx], currentPos, initialAdvance);
+						  &elements[state->elemIdx], currentPos);
 
 		if (skipState != NULL)
 		{
 			nfa_route_to_elem(winstate, ctx, skipState,
-							  &elements[elem->jump], currentPos, initialAdvance);
+							  &elements[elem->jump], currentPos);
 		}
 	}
 }
@@ -6176,7 +6182,7 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6199,7 +6205,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 		state->elemIdx = elem->jump;
 		jumpElem = &elements[state->elemIdx];
 		nfa_route_to_elem(winstate, ctx, state, jumpElem,
-						  currentPos, initialAdvance);
+						  currentPos);
 
 		/*
 		 * Fast-forward fallback for nullable bodies.  E.g. (A?){2,3} when A
@@ -6223,7 +6229,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 				ffState->counts[nextElem->depth]++;
 
 			nfa_route_to_elem(winstate, ctx, ffState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 	else if (elem->max != RPR_QUANTITY_INF && count >= elem->max)
@@ -6239,7 +6245,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 		if (RPRElemIsEnd(nextElem) && state->counts[nextElem->depth] < RPR_COUNT_MAX)
 			state->counts[nextElem->depth]++;
 
-		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos, initialAdvance);
+		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos);
 	}
 	else
 	{
@@ -6277,7 +6283,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Exit first (preferred for reluctant) */
 			nfa_route_to_elem(winstate, ctx, exitState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 
 			/*
 			 * If exit path reached FIN, shortest match is found. Skip loop to
@@ -6291,16 +6297,16 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Loop second */
 			nfa_route_to_elem(winstate, ctx, state, jumpElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 		else
 		{
 			/* Loop first (preferred for greedy) */
 			nfa_route_to_elem(winstate, ctx, state, jumpElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 			/* Exit second */
 			nfa_route_to_elem(winstate, ctx, exitState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 }
@@ -6314,7 +6320,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6359,7 +6365,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Exit first (preferred for reluctant) */
 			nfa_route_to_elem(winstate, ctx, cloneState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 
 			/*
 			 * If exit path reached FIN, the shortest match is found. Skip
@@ -6400,7 +6406,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 			}
 
 			nfa_route_to_elem(winstate, ctx, state, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 	else if (canLoop)
@@ -6424,7 +6430,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 				state->counts[nextElem->depth]++;
 		}
 
-		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos, initialAdvance);
+		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos);
 	}
 }
 
@@ -6436,7 +6442,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
  */
 static void
 nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
-				  RPRNFAState *state, int64 currentPos, bool initialAdvance)
+				  RPRNFAState *state, int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elem;
@@ -6458,28 +6464,25 @@ nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
 	switch (elem->varId)
 	{
 		case RPR_VARID_FIN:
-			/* FIN: record match (skip for initial advance) */
-			if (!initialAdvance)
-				nfa_add_matched_state(winstate, ctx, state, currentPos);
-			else
-				nfa_state_free(winstate, state);
+			/* FIN: record match */
+			nfa_add_matched_state(winstate, ctx, state, currentPos);
 			break;
 
 		case RPR_VARID_ALT:
-			nfa_advance_alt(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_alt(winstate, ctx, state, elem, currentPos);
 			break;
 
 		case RPR_VARID_BEGIN:
-			nfa_advance_begin(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_begin(winstate, ctx, state, elem, currentPos);
 			break;
 
 		case RPR_VARID_END:
-			nfa_advance_end(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_end(winstate, ctx, state, elem, currentPos);
 			break;
 
 		default:
 			/* VAR element */
-			nfa_advance_var(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_var(winstate, ctx, state, elem, currentPos);
 			break;
 	}
 }
@@ -6489,13 +6492,12 @@ nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
  *
  * Advance phase (divergence): transition from all surviving states.
  * Called after match phase with matched VAR states, or at context creation
- * for initial epsilon expansion (initialAdvance=true skips FIN matches).
+ * for initial epsilon expansion (with currentPos = startPos - 1).
  *
  * Processes states in order, using recursive DFS to maintain lexical order.
  */
 static void
-nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos,
-			bool initialAdvance)
+nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos)
 {
 	RPRNFAState *states = ctx->states;
 	RPRNFAState *state;
@@ -6515,7 +6517,7 @@ nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos,
 		states = states->next;
 		state->next = NULL;
 
-		nfa_advance_state(winstate, ctx, state, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, state, currentPos);
 
 		/*
 		 * Early termination: if a FIN was newly reached in this advance,
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index a7c536625f1..2fefb933c71 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -1090,9 +1090,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1124,9 +1124,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1192,9 +1192,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1382,9 +1382,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1460,9 +1460,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index ef184b7950b..d5cd80f423e 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -3347,7 +3347,7 @@ WINDOW w AS (
    Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
    Pattern: ((a' b')+" c)*
    Storage: Memory  Maximum Storage: NkB
-   NFA States: 7 peak, 178 total, 0 merged
+   NFA States: 9 peak, 178 total, 0 merged
    NFA Contexts: 4 peak, 61 total, 22 pruned
    NFA: 1 matched (len 57/57/57.0), 0 mismatched
    NFA: 0 absorbed, 37 skipped (len 1/3/2.0)
@@ -3384,7 +3384,7 @@ WINDOW w AS (
    Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
    Pattern: (a (b c)+)*
    Storage: Memory  Maximum Storage: NkB
-   NFA States: 5 peak, 160 total, 0 merged
+   NFA States: 7 peak, 160 total, 0 merged
    NFA Contexts: 4 peak, 61 total, 22 pruned
    NFA: 1 matched (len 57/57/57.0), 0 mismatched
    NFA: 0 absorbed, 37 skipped (len 1/3/2.0)
-- 
2.50.1 (Apple Git-155)



Attachments:

  [text/plain] nocfbot-0001-fix-elog-message-to-use-lowercase-per-postgresql-con.txt (729B, 3-nocfbot-0001-fix-elog-message-to-use-lowercase-per-postgresql-con.txt)
  download | inline diff:
From f4abb464c187ef6d1334d856db881d40069531da Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 15:49:54 +0900
Subject: [PATCH 1/8] Fix elog message to use lowercase per PostgreSQL
 convention


diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 701e83b519b..4441b4d9c51 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -4710,7 +4710,7 @@ row_is_in_reduced_frame(WindowObject winobj, int64 pos)
 			break;
 
 		default:
-			elog(ERROR, "Unrecognized state: %d at: " INT64_FORMAT,
+			elog(ERROR, "unrecognized state: %d at: " INT64_FORMAT,
 				 state, pos);
 			break;
 	}
-- 
2.50.1 (Apple Git-155)



  [text/plain] nocfbot-0002-fix-rpr-reluctant-quantifier-flag-lost-during-view-s.txt (9.4K, 4-nocfbot-0002-fix-rpr-reluctant-quantifier-flag-lost-during-view-s.txt)
  download | inline diff:
From 2bc7f29dcfceefc83fd787eb138c96247f565360 Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Tue, 3 Mar 2026 15:50:48 +0900
Subject: [PATCH 2/8] Fix RPR reluctant quantifier flag lost during VIEW
 serialization


diff --git a/src/backend/optimizer/plan/rpr.c b/src/backend/optimizer/plan/rpr.c
index 8792d8174fa..009c0f5019d 100644
--- a/src/backend/optimizer/plan/rpr.c
+++ b/src/backend/optimizer/plan/rpr.c
@@ -198,7 +198,7 @@ flattenSeqChildren(List *children)
 
 		/* GROUP{1,1} should have been unwrapped by optimizeGroupPattern */
 		Assert(!(opt->nodeType == RPR_PATTERN_GROUP &&
-				 opt->min == 1 && opt->max == 1 && opt->reluctant < 0));
+				 opt->min == 1 && opt->max == 1 && opt->reluctant == false));
 
 		if (opt->nodeType == RPR_PATTERN_SEQ)
 		{
@@ -234,7 +234,7 @@ mergeConsecutiveVars(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_VAR && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_VAR && child->reluctant == false)
 		{
 			/* ----------------------
 			 * Can merge consecutive VAR nodes if:
@@ -253,7 +253,7 @@ mergeConsecutiveVars(List *children)
 				 * Merge: accumulate min/max into prev. prev is guaranteed to
 				 * be a non-reluctant VAR by the outer condition.
 				 */
-				Assert(prev->nodeType == RPR_PATTERN_VAR && prev->reluctant < 0);
+				Assert(prev->nodeType == RPR_PATTERN_VAR && prev->reluctant == false);
 
 				prev->min += child->min;
 
@@ -308,7 +308,7 @@ mergeConsecutiveGroups(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant == false)
 		{
 			/* ----------------------
 			 * Can merge consecutive GROUP nodes if:
@@ -327,7 +327,7 @@ mergeConsecutiveGroups(List *children)
 				 * Merge: accumulate min/max into prev. prev is guaranteed to
 				 * be a non-reluctant GROUP by the outer condition.
 				 */
-				Assert(prev->nodeType == RPR_PATTERN_GROUP && prev->reluctant < 0);
+				Assert(prev->nodeType == RPR_PATTERN_GROUP && prev->reluctant == false);
 
 				prev->min += child->min;
 
@@ -385,7 +385,7 @@ mergeConsecutiveAlts(List *children)
 	{
 		RPRPatternNode *child = (RPRPatternNode *) lfirst(lc);
 
-		if (child->nodeType == RPR_PATTERN_ALT && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_ALT && child->reluctant == false)
 		{
 			if (prev != NULL &&
 				rprPatternChildrenEqual(prev->children, child->children))
@@ -406,7 +406,8 @@ mergeConsecutiveAlts(List *children)
 						group->nodeType = RPR_PATTERN_GROUP;
 						group->min = count;
 						group->max = count;
-						group->reluctant = -1;
+						group->reluctant = false;
+						group->reluctant_location = -1;
 						group->location = -1;
 						group->children = list_make1(prev);
 						mergedChildren = lappend(mergedChildren, group);
@@ -430,7 +431,8 @@ mergeConsecutiveAlts(List *children)
 					group->nodeType = RPR_PATTERN_GROUP;
 					group->min = count;
 					group->max = count;
-					group->reluctant = -1;
+					group->reluctant = false;
+					group->reluctant_location = -1;
 					group->location = -1;
 					group->children = list_make1(prev);
 					mergedChildren = lappend(mergedChildren, group);
@@ -454,7 +456,8 @@ mergeConsecutiveAlts(List *children)
 			group->nodeType = RPR_PATTERN_GROUP;
 			group->min = count;
 			group->max = count;
-			group->reluctant = -1;
+			group->reluctant = false;
+			group->reluctant_location = -1;
 			group->location = -1;
 			group->children = list_make1(prev);
 			mergedChildren = lappend(mergedChildren, group);
@@ -498,7 +501,7 @@ mergeGroupPrefixSuffix(List *children)
 		 * children. GROUP's content may be wrapped in a SEQ - unwrap for
 		 * comparison.
 		 */
-		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant < 0)
+		if (child->nodeType == RPR_PATTERN_GROUP && child->reluctant == false)
 		{
 			List	   *groupContent = child->children;
 			int			groupChildCount;
@@ -773,14 +776,14 @@ tryMultiplyQuantifiers(RPRPatternNode *pattern)
 	/* Parser always creates GROUP with exactly one child */
 	Assert(list_length(pattern->children) == 1);
 
-	if (pattern->reluctant >= 0)
+	if (pattern->reluctant)
 		return pattern;
 
 	child = (RPRPatternNode *) linitial(pattern->children);
 
 	if ((child->nodeType != RPR_PATTERN_VAR &&
 		 child->nodeType != RPR_PATTERN_GROUP) ||
-		child->reluctant >= 0)
+		child->reluctant)
 		return pattern;
 
 	/* Case 1: Both unbounded - (A*)* -> A*, (A+)+ -> A+ */
@@ -862,11 +865,12 @@ tryUnwrapGroup(RPRPatternNode *pattern)
 	 * the child and unwrap.  E.g., (A)?? -> A??, (A)+? -> A+?
 	 */
 	if (child->nodeType == RPR_PATTERN_VAR &&
-		child->min == 1 && child->max == 1 && child->reluctant < 0)
+		child->min == 1 && child->max == 1 && child->reluctant == false)
 	{
 		child->min = pattern->min;
 		child->max = pattern->max;
 		child->reluctant = pattern->reluctant;
+		child->reluctant_location = pattern->reluctant_location;
 		return child;
 	}
 
@@ -1150,7 +1154,7 @@ fillRPRPatternVar(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth dept
 	elem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 	elem->next = RPR_ELEMIDX_INVALID;
 	elem->jump = RPR_ELEMIDX_INVALID;
-	if (node->reluctant >= 0)
+	if (node->reluctant)
 		elem->flags |= RPR_ELEM_RELUCTANT;
 	(*idx)++;
 
@@ -1189,7 +1193,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		elem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 		elem->next = RPR_ELEMIDX_INVALID;	/* set by finalize */
 		elem->jump = RPR_ELEMIDX_INVALID;	/* set after END */
-		if (node->reluctant >= 0)
+		if (node->reluctant)
 			elem->flags |= RPR_ELEM_RELUCTANT;
 		(*idx)++;
 		groupStartIdx = *idx;	/* children start after BEGIN */
@@ -1214,7 +1218,7 @@ fillRPRPatternGroup(RPRPatternNode *node, RPRPattern *pat, int *idx, RPRDepth de
 		endElem->max = (node->max == INT_MAX) ? RPR_QUANTITY_INF : node->max;
 		endElem->next = RPR_ELEMIDX_INVALID;
 		endElem->jump = groupStartIdx;	/* loop to first child */
-		if (node->reluctant >= 0)
+		if (node->reluctant)
 			endElem->flags |= RPR_ELEM_RELUCTANT;
 
 		/*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b55a11cc837..f1a71cd036b 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -16967,7 +16967,8 @@ row_pattern_alt:
 						n->children = list_make2($1, $3);
 						n->min = 1;
 						n->max = 1;
-						n->reluctant = -1;
+						n->reluctant = false;
+						n->reluctant_location = -1;
 						n->location = @1;
 						$$ = (Node *) n;
 					}
@@ -16994,7 +16995,8 @@ row_pattern_seq:
 						n->children = list_make2($1, $2);
 						n->min = 1;
 						n->max = 1;
-						n->reluctant = -1;
+						n->reluctant = false;
+						n->reluctant_location = -1;
 						n->location = @1;
 						$$ = (Node *) n;
 					}
@@ -17010,6 +17012,7 @@ row_pattern_term:
 					n->min = q->min;
 					n->max = q->max;
 					n->reluctant = q->reluctant;
+					n->reluctant_location = q->reluctant_location;
 					$$ = (Node *) n;
 				}
 		;
@@ -17022,7 +17025,8 @@ row_pattern_primary:
 					n->varName = $1;
 					n->min = 1;
 					n->max = 1;
-					n->reluctant = -1;
+					n->reluctant = false;
+					n->reluctant_location = -1;
 					n->children = NIL;
 					n->location = @1;
 					$$ = (Node *) n;
@@ -17035,7 +17039,8 @@ row_pattern_primary:
 					n->children = list_make1(inner);
 					n->min = 1;
 					n->max = 1;
-					n->reluctant = -1;
+					n->reluctant = false;
+					n->reluctant_location = -1;
 					n->location = @1;
 					$$ = (Node *) n;
 				}
@@ -20400,14 +20405,15 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query)
  *		Create an RPRPatternNode with specified quantifier bounds.
  */
 static RPRPatternNode *
-makeRPRQuantifier(int min, int max, ParseLoc reluctant, int location,
+makeRPRQuantifier(int min, int max, ParseLoc reluctant_location, int location,
 				  core_yyscan_t yyscanner)
 {
 	RPRPatternNode *n = makeNode(RPRPatternNode);
 
 	n->min = min;
 	n->max = max;
-	n->reluctant = reluctant;
+	n->reluctant = (reluctant_location >= 0);
+	n->reluctant_location = reluctant_location;
 	n->location = location;
 	return n;
 }
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f5bb81e8c05..928bed2e9fb 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6778,7 +6778,7 @@ append_pattern_quantifier(StringInfo buf, RPRPatternNode *node)
 	else
 		appendStringInfo(buf, "{%d,%d}", node->min, node->max);
 
-	if (node->reluctant >= 0)
+	if (node->reluctant)
 	{
 		if (!has_quantifier)
 			appendStringInfo(buf, "{1}");	/* make reluctant ? unambiguous */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 8af527b57d3..22e856c671d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -608,8 +608,8 @@ typedef struct RPRPatternNode
 	RPRPatternNodeType nodeType;	/* VAR, SEQ, ALT, GROUP */
 	int			min;			/* minimum repetitions (0 for *, ?) */
 	int			max;			/* maximum repetitions (INT_MAX for *, +) */
-	ParseLoc	reluctant;		/* location of '?' for reluctant, -1 for
-								 * greedy */
+	bool		reluctant;		/* true for reluctant (non-greedy) */
+	ParseLoc	reluctant_location; /* location of '?' token, or -1 */
 	ParseLoc	location;		/* token location, or -1 */
 	char	   *varName;		/* VAR: variable name */
 	List	   *children;		/* SEQ, ALT, GROUP: child nodes */
-- 
2.50.1 (Apple Git-155)



  [text/plain] nocfbot-0003-expand-rpr-test-coverage-and-improve-test-comments.txt (265.5K, 5-nocfbot-0003-expand-rpr-test-coverage-and-improve-test-comments.txt)
  download

  [text/plain] nocfbot-0004-keep-rpr-test-objects-for-pg_upgrade-pg_dump-testing.txt (150.8K, 6-nocfbot-0004-keep-rpr-test-objects-for-pg_upgrade-pg_dump-testing.txt)
  download

  [text/plain] nocfbot-0005-disable-run-condition-pushdown-for-rpr-windows.txt (1.6K, 7-nocfbot-0005-disable-run-condition-pushdown-for-rpr-windows.txt)
  download | inline diff:
From 81a9b83b9d61704e85d97bc79b10ac6a00d73def Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 15:53:33 +0900
Subject: [PATCH 5/8] Disable run condition pushdown for RPR windows


diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 90275e25872..b67c35af39a 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -2453,6 +2453,17 @@ find_window_run_conditions(Query *subquery, AttrNumber attno,
 	wclause = (WindowClause *) list_nth(subquery->windowClause,
 										wfunc->winref - 1);
 
+	/*
+	 * If a DEFINE clause exists, we cannot push down a run condition. In the
+	 * case, a window partition (or frame) is divided into multiple reduced
+	 * frames and each frame should be evaluated to the end of the partition
+	 * (or full frame end). This means we cannot apply the run condition
+	 * optimization because it stops evaluation window functions in certain
+	 * cases.
+	 */
+	if (wclause->defineClause != NIL)
+		return false;
+
 	req.type = T_SupportRequestWFuncMonotonic;
 	req.window_func = wfunc;
 	req.window_clause = wclause;
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index ae6d9c9b937..a7c536625f1 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -2465,7 +2465,9 @@ WHERE cnt > 0
 ORDER BY id;
  id | val | cnt 
 ----+-----+-----
-(0 rows)
+  2 |  20 |   2
+  4 |  40 |   1
+(2 rows)
 
 -- Nested subqueries
 SELECT *
-- 
2.50.1 (Apple Git-155)



  [text/plain] nocfbot-0006-disable-frame-optimization-for-rpr-windows.txt (8.3K, 8-nocfbot-0006-disable-frame-optimization-for-rpr-windows.txt)
  download | inline diff:
From 08910348e4c3770880fa098b50596805b15e38f7 Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 15:59:45 +0900
Subject: [PATCH 6/8] Disable frame optimization for RPR windows


diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 04faf919033..29a02f3affb 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -5907,6 +5907,14 @@ optimize_window_clauses(PlannerInfo *root, WindowFuncLists *wflists)
 		if (wflists->windowFuncs[wc->winref] == NIL)
 			continue;
 
+		/*
+		 * If a DEFINE clause exists, do not let support functions replace the
+		 * frame with a non-RPR-compatible one.  RPR windows require ROWS
+		 * BETWEEN CURRENT ROW AND ...
+		 */
+		if (wc->defineClause != NIL)
+			continue;
+
 		foreach(lc2, wflists->windowFuncs[wc->winref])
 		{
 			SupportRequestOptimizeWindowClause req;
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index 3c70a12874a..ef184b7950b 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -3821,3 +3821,117 @@ WINDOW w AS (
    ->  Function Scan on generate_series s (actual rows=500.00 loops=1)
 (9 rows)
 
+--
+-- Planner optimization: optimize_window_clauses must not alter RPR frame
+--
+-- optimize_window_clauses() replaces frame options via prosupport functions.
+-- Affected functions: row_number, rank, dense_rank, percent_rank, cume_dist,
+-- ntile.  All would change the frame to ROWS UNBOUNDED PRECEDING, breaking
+-- RPR's required ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING.
+-- Test with row_number() as representative case.
+--
+-- Without RPR: row_number() frame is optimized to ROWS UNBOUNDED PRECEDING
+CREATE VIEW rpr_ev87 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+);
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev87;
+                          QUERY PLAN                          
+--------------------------------------------------------------
+ Subquery Scan on rpr_ev87
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS UNBOUNDED PRECEDING)
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(6 rows)
+
+-- With RPR: frame must remain ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+CREATE VIEW rpr_ev88 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE
+        B AS v > PREV(v)
+);
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev88;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on rpr_ev88
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Pattern: a b+
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(7 rows)
+
+--
+-- Planner optimization: find_window_run_conditions must not push down
+-- RPR window function results as Run Conditions.
+--
+-- find_window_run_conditions() pushes WHERE filters on monotonic window
+-- functions into WindowAgg as Run Conditions for early termination.
+-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
+-- FOLLOWING), the monotonic direction determines which operators trigger
+-- Run Condition pushdown:
+--   INCREASING (<=): row_number, rank, dense_rank, percent_rank,
+--                    cume_dist, ntile
+--   DECREASING (>):  count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
+-- RPR window function results are match-dependent, not monotonic.
+-- Test with count(*) > 0 as representative case.
+--
+-- Without RPR: count(*) > 0 is pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    )
+) t WHERE cnt > 0;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on t
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Run Condition: (count(*) OVER w > 0)
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(7 rows)
+
+-- With RPR: count(*) > 0 must not be pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (A B+)
+        DEFINE
+            B AS v > PREV(v)
+    )
+) t WHERE cnt > 0;
+                                      QUERY PLAN                                      
+--------------------------------------------------------------------------------------
+ Subquery Scan on t
+   Filter: (t.cnt > 0)
+   ->  WindowAgg
+         Window: w AS (ORDER BY s.v ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+         Pattern: a b+
+         ->  Sort
+               Sort Key: s.v
+               ->  Function Scan on generate_series s
+(8 rows)
+
diff --git a/src/test/regress/sql/rpr_explain.sql b/src/test/regress/sql/rpr_explain.sql
index 8e22382a68e..640d328957b 100644
--- a/src/test/regress/sql/rpr_explain.sql
+++ b/src/test/regress/sql/rpr_explain.sql
@@ -2196,3 +2196,81 @@ WINDOW w AS (
         E AS v % 100 = 5
 );');
 
+--
+-- Planner optimization: optimize_window_clauses must not alter RPR frame
+--
+-- optimize_window_clauses() replaces frame options via prosupport functions.
+-- Affected functions: row_number, rank, dense_rank, percent_rank, cume_dist,
+-- ntile.  All would change the frame to ROWS UNBOUNDED PRECEDING, breaking
+-- RPR's required ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING.
+-- Test with row_number() as representative case.
+--
+
+-- Without RPR: row_number() frame is optimized to ROWS UNBOUNDED PRECEDING
+CREATE VIEW rpr_ev87 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+);
+
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev87;
+
+-- With RPR: frame must remain ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+CREATE VIEW rpr_ev88 AS
+SELECT row_number() OVER w
+FROM generate_series(1, 10) AS s(v)
+WINDOW w AS (
+    ORDER BY v
+    ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    AFTER MATCH SKIP PAST LAST ROW
+    PATTERN (A B+)
+    DEFINE
+        B AS v > PREV(v)
+);
+
+EXPLAIN (COSTS OFF) SELECT * FROM rpr_ev88;
+
+--
+-- Planner optimization: find_window_run_conditions must not push down
+-- RPR window function results as Run Conditions.
+--
+-- find_window_run_conditions() pushes WHERE filters on monotonic window
+-- functions into WindowAgg as Run Conditions for early termination.
+-- With RPR's required frame (ROWS BETWEEN CURRENT ROW AND UNBOUNDED
+-- FOLLOWING), the monotonic direction determines which operators trigger
+-- Run Condition pushdown:
+--   INCREASING (<=): row_number, rank, dense_rank, percent_rank,
+--                    cume_dist, ntile
+--   DECREASING (>):  count(*) (via int8inc, END_UNBOUNDED_FOLLOWING)
+-- RPR window function results are match-dependent, not monotonic.
+-- Test with count(*) > 0 as representative case.
+--
+
+-- Without RPR: count(*) > 0 is pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+    )
+) t WHERE cnt > 0;
+
+-- With RPR: count(*) > 0 must not be pushed down as Run Condition
+EXPLAIN (COSTS OFF)
+SELECT * FROM (
+    SELECT count(*) OVER w AS cnt
+    FROM generate_series(1, 10) AS s(v)
+    WINDOW w AS (
+        ORDER BY v
+        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
+        AFTER MATCH SKIP PAST LAST ROW
+        PATTERN (A B+)
+        DEFINE
+            B AS v > PREV(v)
+    )
+) t WHERE cnt > 0;
+
-- 
2.50.1 (Apple Git-155)



  [text/plain] nocfbot-0007-add-stock-scenario-tests-for-rpr-pattern-matching.txt (103.5K, 9-nocfbot-0007-add-stock-scenario-tests-for-rpr-pattern-matching.txt)
  download

  [text/plain] nocfbot-0008-fix-zero-min-reluctant-quantifier-to-produce-zero-le.txt (15.2K, 10-nocfbot-0008-fix-zero-min-reluctant-quantifier-to-produce-zero-le.txt)
  download | inline diff:
From d13cf490ce7585fd99a7690911a75d90a51017bb Mon Sep 17 00:00:00 2001
From: Henson Choi <[email protected]>
Date: Wed, 4 Mar 2026 14:08:47 +0900
Subject: [PATCH 8/8] Fix zero-min reluctant quantifier to produce zero-length
 match


diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 4441b4d9c51..3075c51789c 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -307,24 +307,24 @@ static inline bool nfa_eval_var_match(WindowAggState *winstate,
 static void nfa_match(WindowAggState *winstate, RPRNFAContext *ctx,
 					  bool *varMatched);
 static void nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
-							  RPRNFAState *state, int64 currentPos, bool initialAdvance);
+							  RPRNFAState *state, int64 currentPos);
 static void nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 							  RPRNFAState *state, RPRPatternElement *nextElem,
-							  int64 currentPos, bool initialAdvance);
+							  int64 currentPos);
 static void nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 							  RPRNFAState *state, RPRPatternElement *elem,
-							  int64 currentPos, bool initialAdvance);
+							  int64 currentPos);
 static void nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 							RPRNFAState *state, RPRPatternElement *elem,
-							int64 currentPos, bool initialAdvance);
+							int64 currentPos);
 static void nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx,
-						int64 currentPos, bool initialAdvance);
+						int64 currentPos);
 
 /*
  * Not null info bit array consists of 2-bit items
@@ -5008,9 +5008,14 @@ register_result:
  *   handles nested groups like "((A|B)+)+" correctly - exiting the inner
  *   group counts as one iteration of the outer group.
  *
- * - initialAdvance flag: The first advance after context creation must
- *   skip FIN recording. Reaching FIN without evaluating any VAR would
- *   create a zero-length match, which is invalid.
+ * - Zero-length match handling: The initial advance uses currentPos =
+ *   startPos - 1 (before any row is consumed). If FIN is reached via
+ *   epsilon transitions alone, matchEndRow = startPos - 1 < matchStartRow,
+ *   resulting in UNMATCHED. For reluctant min=0 patterns (A*?, A??),
+ *   the skip path reaches FIN first and early termination prunes enter
+ *   paths, yielding an immediate zero-length (unmatched) result. For
+ *   greedy patterns (A*), the enter path adds VAR states first, then
+ *   the skip FIN is recorded but VAR states survive for later matching.
  *
  * Context Absorption Runtime:
  * ---------------------------
@@ -5122,7 +5127,7 @@ nfa_process_row(WindowAggState *winstate, int64 currentPos,
 		Assert(!hasLimitedFrame ||
 			   currentPos < ctx->matchStartRow + frameOffset + 1);
 
-		nfa_advance(winstate, ctx, currentPos, false);
+		nfa_advance(winstate, ctx, currentPos);
 	}
 }
 
@@ -5467,10 +5472,11 @@ nfa_start_context(WindowAggState *winstate, int64 startPos)
 	 * states for VAR elements with min=0. This prepares the context for the
 	 * first row's match phase.
 	 *
-	 * Pass initialAdvance=true to prevent recording zero-length matches when
-	 * optional patterns can skip all VARs to reach FIN immediately.
+	 * Use startPos - 1 as currentPos since no row has been consumed yet. If
+	 * FIN is reached via epsilon transitions, matchEndRow = startPos - 1
+	 * which is less than matchStartRow, resulting in UNMATCHED treatment.
 	 */
-	nfa_advance(winstate, ctx, startPos, true);
+	nfa_advance(winstate, ctx, startPos - 1);
 
 	return ctx;
 }
@@ -5711,7 +5717,7 @@ nfa_finalize_all_contexts(WindowAggState *winstate, int64 lastPos)
 		if (ctx->states != NULL)
 		{
 			nfa_match(winstate, ctx, NULL);
-			nfa_advance(winstate, ctx, lastPos, false);
+			nfa_advance(winstate, ctx, lastPos);
 		}
 	}
 }
@@ -6047,7 +6053,7 @@ nfa_match(WindowAggState *winstate, RPRNFAContext *ctx, bool *varMatched)
 static void
 nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 				  RPRNFAState *state, RPRPatternElement *nextElem,
-				  int64 currentPos, bool initialAdvance)
+				  int64 currentPos)
 {
 	if (RPRElemIsVar(nextElem))
 	{
@@ -6061,11 +6067,11 @@ nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 		nfa_add_state_unique(winstate, ctx, state);
 
 		if (skipState != NULL)
-			nfa_advance_state(winstate, ctx, skipState, currentPos, initialAdvance);
+			nfa_advance_state(winstate, ctx, skipState, currentPos);
 	}
 	else
 	{
-		nfa_advance_state(winstate, ctx, state, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, state, currentPos);
 	}
 }
 
@@ -6077,7 +6083,7 @@ nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6097,7 +6103,7 @@ nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 									state->counts, state->isAbsorbable);
 
 		/* Recursively process this branch before next */
-		nfa_advance_state(winstate, ctx, newState, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, newState, currentPos);
 		altIdx = altElem->jump;
 	}
 
@@ -6115,7 +6121,7 @@ nfa_advance_alt(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 				  RPRNFAState *state, RPRPatternElement *elem,
-				  int64 currentPos, bool initialAdvance)
+				  int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6136,7 +6142,7 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 
 		/* Reluctant: skip first (prefer fewer iterations), enter second */
 		nfa_route_to_elem(winstate, ctx, skipState,
-						  &elements[elem->jump], currentPos, initialAdvance);
+						  &elements[elem->jump], currentPos);
 
 		/*
 		 * If skip path reached FIN, shortest match is found. Skip group entry
@@ -6150,19 +6156,19 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 
 		state->elemIdx = elem->next;
 		nfa_route_to_elem(winstate, ctx, state,
-						  &elements[state->elemIdx], currentPos, initialAdvance);
+						  &elements[state->elemIdx], currentPos);
 	}
 	else
 	{
 		/* Greedy: enter first, skip second */
 		state->elemIdx = elem->next;
 		nfa_route_to_elem(winstate, ctx, state,
-						  &elements[state->elemIdx], currentPos, initialAdvance);
+						  &elements[state->elemIdx], currentPos);
 
 		if (skipState != NULL)
 		{
 			nfa_route_to_elem(winstate, ctx, skipState,
-							  &elements[elem->jump], currentPos, initialAdvance);
+							  &elements[elem->jump], currentPos);
 		}
 	}
 }
@@ -6176,7 +6182,7 @@ nfa_advance_begin(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6199,7 +6205,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 		state->elemIdx = elem->jump;
 		jumpElem = &elements[state->elemIdx];
 		nfa_route_to_elem(winstate, ctx, state, jumpElem,
-						  currentPos, initialAdvance);
+						  currentPos);
 
 		/*
 		 * Fast-forward fallback for nullable bodies.  E.g. (A?){2,3} when A
@@ -6223,7 +6229,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 				ffState->counts[nextElem->depth]++;
 
 			nfa_route_to_elem(winstate, ctx, ffState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 	else if (elem->max != RPR_QUANTITY_INF && count >= elem->max)
@@ -6239,7 +6245,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 		if (RPRElemIsEnd(nextElem) && state->counts[nextElem->depth] < RPR_COUNT_MAX)
 			state->counts[nextElem->depth]++;
 
-		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos, initialAdvance);
+		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos);
 	}
 	else
 	{
@@ -6277,7 +6283,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Exit first (preferred for reluctant) */
 			nfa_route_to_elem(winstate, ctx, exitState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 
 			/*
 			 * If exit path reached FIN, shortest match is found. Skip loop to
@@ -6291,16 +6297,16 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Loop second */
 			nfa_route_to_elem(winstate, ctx, state, jumpElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 		else
 		{
 			/* Loop first (preferred for greedy) */
 			nfa_route_to_elem(winstate, ctx, state, jumpElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 			/* Exit second */
 			nfa_route_to_elem(winstate, ctx, exitState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 }
@@ -6314,7 +6320,7 @@ nfa_advance_end(WindowAggState *winstate, RPRNFAContext *ctx,
 static void
 nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 				RPRNFAState *state, RPRPatternElement *elem,
-				int64 currentPos, bool initialAdvance)
+				int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elements = pattern->elements;
@@ -6359,7 +6365,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 
 			/* Exit first (preferred for reluctant) */
 			nfa_route_to_elem(winstate, ctx, cloneState, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 
 			/*
 			 * If exit path reached FIN, the shortest match is found. Skip
@@ -6400,7 +6406,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 			}
 
 			nfa_route_to_elem(winstate, ctx, state, nextElem,
-							  currentPos, initialAdvance);
+							  currentPos);
 		}
 	}
 	else if (canLoop)
@@ -6424,7 +6430,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
 				state->counts[nextElem->depth]++;
 		}
 
-		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos, initialAdvance);
+		nfa_route_to_elem(winstate, ctx, state, nextElem, currentPos);
 	}
 }
 
@@ -6436,7 +6442,7 @@ nfa_advance_var(WindowAggState *winstate, RPRNFAContext *ctx,
  */
 static void
 nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
-				  RPRNFAState *state, int64 currentPos, bool initialAdvance)
+				  RPRNFAState *state, int64 currentPos)
 {
 	RPRPattern *pattern = winstate->rpPattern;
 	RPRPatternElement *elem;
@@ -6458,28 +6464,25 @@ nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
 	switch (elem->varId)
 	{
 		case RPR_VARID_FIN:
-			/* FIN: record match (skip for initial advance) */
-			if (!initialAdvance)
-				nfa_add_matched_state(winstate, ctx, state, currentPos);
-			else
-				nfa_state_free(winstate, state);
+			/* FIN: record match */
+			nfa_add_matched_state(winstate, ctx, state, currentPos);
 			break;
 
 		case RPR_VARID_ALT:
-			nfa_advance_alt(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_alt(winstate, ctx, state, elem, currentPos);
 			break;
 
 		case RPR_VARID_BEGIN:
-			nfa_advance_begin(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_begin(winstate, ctx, state, elem, currentPos);
 			break;
 
 		case RPR_VARID_END:
-			nfa_advance_end(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_end(winstate, ctx, state, elem, currentPos);
 			break;
 
 		default:
 			/* VAR element */
-			nfa_advance_var(winstate, ctx, state, elem, currentPos, initialAdvance);
+			nfa_advance_var(winstate, ctx, state, elem, currentPos);
 			break;
 	}
 }
@@ -6489,13 +6492,12 @@ nfa_advance_state(WindowAggState *winstate, RPRNFAContext *ctx,
  *
  * Advance phase (divergence): transition from all surviving states.
  * Called after match phase with matched VAR states, or at context creation
- * for initial epsilon expansion (initialAdvance=true skips FIN matches).
+ * for initial epsilon expansion (with currentPos = startPos - 1).
  *
  * Processes states in order, using recursive DFS to maintain lexical order.
  */
 static void
-nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos,
-			bool initialAdvance)
+nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos)
 {
 	RPRNFAState *states = ctx->states;
 	RPRNFAState *state;
@@ -6515,7 +6517,7 @@ nfa_advance(WindowAggState *winstate, RPRNFAContext *ctx, int64 currentPos,
 		states = states->next;
 		state->next = NULL;
 
-		nfa_advance_state(winstate, ctx, state, currentPos, initialAdvance);
+		nfa_advance_state(winstate, ctx, state, currentPos);
 
 		/*
 		 * Early termination: if a FIN was newly reached in this advance,
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index a7c536625f1..2fefb933c71 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -1090,9 +1090,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1124,9 +1124,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1192,9 +1192,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1382,9 +1382,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
@@ -1460,9 +1460,9 @@ WINDOW w AS (
 );
  count 
 -------
-     1
-     1
-     1
+     0
+     0
+     0
 (3 rows)
 
 -- Reluctant quantifier: prefer shortest match
diff --git a/src/test/regress/expected/rpr_explain.out b/src/test/regress/expected/rpr_explain.out
index ef184b7950b..d5cd80f423e 100644
--- a/src/test/regress/expected/rpr_explain.out
+++ b/src/test/regress/expected/rpr_explain.out
@@ -3347,7 +3347,7 @@ WINDOW w AS (
    Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
    Pattern: ((a' b')+" c)*
    Storage: Memory  Maximum Storage: NkB
-   NFA States: 7 peak, 178 total, 0 merged
+   NFA States: 9 peak, 178 total, 0 merged
    NFA Contexts: 4 peak, 61 total, 22 pruned
    NFA: 1 matched (len 57/57/57.0), 0 mismatched
    NFA: 0 absorbed, 37 skipped (len 1/3/2.0)
@@ -3384,7 +3384,7 @@ WINDOW w AS (
    Window: w AS (ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
    Pattern: (a (b c)+)*
    Storage: Memory  Maximum Storage: NkB
-   NFA States: 5 peak, 160 total, 0 merged
+   NFA States: 7 peak, 160 total, 0 merged
    NFA Contexts: 4 peak, 61 total, 22 pruned
    NFA: 1 matched (len 57/57/57.0), 0 mismatched
    NFA: 0 absorbed, 37 skipped (len 1/3/2.0)
-- 
2.50.1 (Apple Git-155)



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]
  Subject: Re: Row pattern recognition
  In-Reply-To: <CAAAe_zBH=6nh6Yg9EuohthTgzHgtbhtvLYAnabg6mCHQPLLpqQ@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