From 2db769b38bb4b339f57a9d2cff6bdd8f1305c551 Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Mon, 23 Feb 2026 14:56:42 +0900
Subject: [PATCH 6/6] Allow A{0} quantifier

---
 src/backend/executor/nodeWindowAgg.c   |  7 +++++++
 src/backend/parser/gram.y              |  8 +++----
 src/test/regress/expected/rpr_base.out | 29 ++++++++++++++++++--------
 src/test/regress/sql/rpr_base.sql      |  8 +++----
 4 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 98546fb17f7..72ded5efc1f 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -6070,6 +6070,13 @@ nfa_route_to_elem(WindowAggState *winstate, RPRNFAContext *ctx,
 {
 	if (RPRElemIsVar(nextElem))
 	{
+		if (nextElem->max == 0)
+		{
+			/* max=0: variable never matches, treat as epsilon transition */
+			state->elemIdx = nextElem->next;
+			nfa_advance_state(winstate, ctx, state, currentPos, initialAdvance);
+			return;
+		}
 		nfa_add_state_unique(winstate, ctx, state);
 		if (RPRElemCanSkip(nextElem))
 		{
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5a3d141e992..17503b38e3a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -17070,10 +17070,10 @@ row_pattern_quantifier_opt:
 			/* {n}, {n,}, {,m}, {n,m} quantifiers */
 			| '{' Iconst '}'
 				{
-					if ($2 <= 0 || $2 >= INT_MAX)
+					if ($2 < 0 || $2 >= INT_MAX)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", INT_MAX - 1),
+								errmsg("quantifier bound must be between 0 and %d", INT_MAX - 1),
 								parser_errposition(@2));
 					$$ = (Node *) makeRPRQuantifier($2, $2, -1, @1, yyscanner);
 				}
@@ -17118,10 +17118,10 @@ row_pattern_quantifier_opt:
 								 errmsg("invalid token after range quantifier"),
 								 errhint("Only \"?\" is allowed after {n} to make it reluctant."),
 								 parser_errposition(@4)));
-					if ($2 <= 0 || $2 >= INT_MAX)
+					if ($2 < 0 || $2 >= INT_MAX)
 						ereport(ERROR,
 								errcode(ERRCODE_SYNTAX_ERROR),
-								errmsg("quantifier bound must be between 1 and %d", INT_MAX - 1),
+								errmsg("quantifier bound must be between 0 and %d", INT_MAX - 1),
 								parser_errposition(@2));
 					$$ = (Node *) makeRPRQuantifier($2, $2, @4, @1, yyscanner);
 				}
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index 43eea32130f..7df71aaa904 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -923,7 +923,7 @@ ORDER BY id;
 (10 rows)
 
 -- Edge case quantifiers
--- {0} is not allowed (min must be >= 1)
+-- {0} always skips the variable (zero iterations)
 SELECT id, val, COUNT(*) OVER w as cnt
 FROM rpr_quant
 WINDOW w AS (
@@ -933,10 +933,21 @@ WINDOW w AS (
     DEFINE A AS val > 1000, B AS val > 0
 )
 ORDER BY id;
-ERROR:  quantifier bound must be between 1 and 2147483646
-LINE 6:     PATTERN (A{0} B)
-                       ^
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+ 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)
+
+-- Expected: A{0} skips A, only B matches each row
 -- {0,0} is not allowed (max must be >= 1)
 SELECT id, val, COUNT(*) OVER w as cnt
 FROM rpr_quant
@@ -1274,10 +1285,10 @@ WINDOW w AS (
     PATTERN (A{2147483647}?)
     DEFINE A AS val > 0
 );
-ERROR:  quantifier bound must be between 1 and 2147483646
+ERROR:  quantifier bound must be between 0 and 2147483646
 LINE 6:     PATTERN (A{2147483647}?)
                        ^
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+-- Expected: ERROR: quantifier bound must be between 0 and 2147483646
 -- {-1,}? (negative lower bound)
 SELECT COUNT(*) OVER w
 FROM rpr_reluctant
@@ -1537,10 +1548,10 @@ WINDOW w AS (
     PATTERN (A{2147483647})
     DEFINE A AS id > 0
 );
-ERROR:  quantifier bound must be between 1 and 2147483646
+ERROR:  quantifier bound must be between 0 and 2147483646
 LINE 6:     PATTERN (A{2147483647})
                        ^
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+-- Expected: ERROR: quantifier bound must be between 0 and 2147483646
 -- {n,} boundary errors
 -- Negative lower bound in {n,}
 SELECT COUNT(*) OVER w
diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql
index 992c081205a..d25ca07adaf 100644
--- a/src/test/regress/sql/rpr_base.sql
+++ b/src/test/regress/sql/rpr_base.sql
@@ -673,7 +673,7 @@ ORDER BY id;
 
 -- Edge case quantifiers
 
--- {0} is not allowed (min must be >= 1)
+-- {0} always skips the variable (zero iterations)
 SELECT id, val, COUNT(*) OVER w as cnt
 FROM rpr_quant
 WINDOW w AS (
@@ -683,7 +683,7 @@ WINDOW w AS (
     DEFINE A AS val > 1000, B AS val > 0
 )
 ORDER BY id;
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+-- Expected: A{0} skips A, only B matches each row
 
 -- {0,0} is not allowed (max must be >= 1)
 SELECT id, val, COUNT(*) OVER w as cnt
@@ -911,7 +911,7 @@ WINDOW w AS (
     PATTERN (A{2147483647}?)
     DEFINE A AS val > 0
 );
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+-- Expected: ERROR: quantifier bound must be between 0 and 2147483646
 
 -- {-1,}? (negative lower bound)
 SELECT COUNT(*) OVER w
@@ -1118,7 +1118,7 @@ WINDOW w AS (
     PATTERN (A{2147483647})
     DEFINE A AS id > 0
 );
--- Expected: ERROR: quantifier bound must be between 1 and 2147483646
+-- Expected: ERROR: quantifier bound must be between 0 and 2147483646
 
 -- {n,} boundary errors
 
-- 
2.50.1 (Apple Git-155)

