nocfbot-0075-Quote-the-offending-token-in-RPR-quantifier-synta.txt
text/plain
Filename: nocfbot-0075-Quote-the-offending-token-in-RPR-quantifier-synta.txt
Type: text/plain
Part: 72
Message:
Re: Row pattern recognition
From 3d015812c7462a8fcfe02d2a34ebdcf773e2c23a Mon Sep 17 00:00:00 2001
From: Henson Choi <assam258@gmail.com>
Date: Wed, 10 Jun 2026 23:46:20 +0900
Subject: [PATCH 75/77] Quote the offending token in RPR quantifier syntax
errors
When an invalid token follows a row pattern quantifier, name the token
in the error message instead of reporting a bare "invalid token after
... quantifier". The lexer glues a quantifier and a trailing
alternation operator into one token, so rpr_invalid_quantifier_token()
strips the trailing '|' before display ("*|" reports '*', "*?|" reports
"*?") while leaving other tokens unchanged. Add negative-case tests.
---
src/backend/parser/gram.y | 36 +++++++++++++++++++++-----
src/test/regress/expected/rpr_base.out | 29 ++++++++++++---------
src/test/regress/sql/rpr_base.sql | 3 ++-
3 files changed, 48 insertions(+), 20 deletions(-)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1a5459fa150..4ae951aaeba 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -214,6 +214,7 @@ static RPRPatternNode *makeRPRSeqOrSingle(List *children, int location);
static RPRPatternNode *splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner);
static RPRPatternNode *makeRPRQuantifier(int32 min, int32 max, bool reluctant,
int location);
+static const char *rpr_invalid_quantifier_token(const char *tok);
%}
@@ -17808,7 +17809,7 @@ row_pattern_quantifier_opt:
else
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after \"*\" quantifier"),
+ errmsg("invalid token \"%s\" after \"*\" quantifier", rpr_invalid_quantifier_token($2)),
errhint("Did you mean \"*?\" for reluctant quantifier?"),
parser_errposition(@2));
}
@@ -17825,7 +17826,7 @@ row_pattern_quantifier_opt:
else
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after \"+\" quantifier"),
+ errmsg("invalid token \"%s\" after \"+\" quantifier", rpr_invalid_quantifier_token($2)),
errhint("Did you mean \"+?\" for reluctant quantifier?"),
parser_errposition(@2));
}
@@ -17834,7 +17835,7 @@ row_pattern_quantifier_opt:
if (strcmp($1, "?") != 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid quantifier combination"),
+ errmsg("invalid quantifier combination: \"%s%s\"", $1, $2),
errhint("Did you mean \"??\" for reluctant quantifier?"),
parser_errposition(@1));
if (strcmp($2, "?") == 0)
@@ -17900,7 +17901,7 @@ row_pattern_quantifier_opt:
if (strcmp($4, "?") != 0 && strcmp($4, "?|") != 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after range quantifier"),
+ errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($4)),
errhint("Only \"?\" is allowed after {n} to make it reluctant."),
parser_errposition(@4));
if ($2 <= 0 || $2 >= PG_INT32_MAX)
@@ -17917,7 +17918,7 @@ row_pattern_quantifier_opt:
if (strcmp($5, "?") != 0 && strcmp($5, "?|") != 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after range quantifier"),
+ errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($5)),
errhint("Only \"?\" is allowed after {n,} or {,m} to make it reluctant."),
parser_errposition(@5));
if ($2 < 0 || $2 >= PG_INT32_MAX)
@@ -17934,7 +17935,7 @@ row_pattern_quantifier_opt:
if (strcmp($5, "?") != 0 && strcmp($5, "?|") != 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after range quantifier"),
+ errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($5)),
errhint("Only \"?\" is allowed after {n,} or {,m} to make it reluctant."),
parser_errposition(@5));
if ($3 <= 0 || $3 >= PG_INT32_MAX)
@@ -17951,7 +17952,7 @@ row_pattern_quantifier_opt:
if (strcmp($6, "?") != 0 && strcmp($6, "?|") != 0)
ereport(ERROR,
errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid token after range quantifier"),
+ errmsg("invalid token \"%s\" after range quantifier", rpr_invalid_quantifier_token($6)),
errhint("Only \"?\" is allowed after {n,m} to make it reluctant."),
parser_errposition(@6));
if ($2 < 0 || $4 <= 0 || $2 >= PG_INT32_MAX || $4 >= PG_INT32_MAX)
@@ -21491,6 +21492,27 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner)
return node;
}
+/*
+ * rpr_invalid_quantifier_token
+ * Return the offending part of an invalid token following a quantifier.
+ *
+ * The lexer glues a quantifier and a trailing alternation operator into a
+ * single token (for example "*|"). When such a glued token appears in an
+ * invalid position, drop the trailing '|': it is the alternation operator,
+ * not part of the offending quantifier, so "*|" reports '*' and "*?|"
+ * reports "*?". Tokens without a trailing '|' (such as "??" or "?+") are
+ * reported unchanged.
+ */
+static const char *
+rpr_invalid_quantifier_token(const char *tok)
+{
+ size_t len = strlen(tok);
+
+ if (len > 1 && tok[len - 1] == '|')
+ return pnstrdup(tok, len - 1);
+ return tok;
+}
+
/* parser_init()
* Initialize to parse one query string
*/
diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out
index 2407c455164..302caea1e5d 100644
--- a/src/test/regress/expected/rpr_base.out
+++ b/src/test/regress/expected/rpr_base.out
@@ -3217,12 +3217,12 @@ ERROR: alternation operator "|" requires a pattern on both sides
LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN ((A*|)) DEFI...
^
SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A* *|B) DEFINE A AS val > 0, B AS val <= 0);
-ERROR: invalid token after "*" quantifier
+ERROR: invalid token "*" after "*" quantifier
LINE 1: ...N CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A* *|B) DEFIN...
^
HINT: Did you mean "*?" for reluctant quantifier?
SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A* *?|B) DEFINE A AS val > 0, B AS val <= 0);
-ERROR: invalid token after "*" quantifier
+ERROR: invalid token "*?" after "*" quantifier
LINE 1: ...N CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A* *?|B) DEFI...
^
HINT: Did you mean "*?" for reluctant quantifier?
@@ -3232,12 +3232,12 @@ LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A? *?|B) DE...
^
HINT: Did you mean "??" for reluctant quantifier?
SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A{2}*?|B) DEFINE A AS val > 0, B AS val <= 0);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "*?" after range quantifier
LINE 1: ... CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A{2}*?|B) DEFI...
^
HINT: Only "?" is allowed after {n} to make it reluctant.
SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A{2} *?|B) DEFINE A AS val > 0, B AS val <= 0);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "*?" after range quantifier
LINE 1: ...CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A{2} *?|B) DEFI...
^
HINT: Only "?" is allowed after {n} to make it reluctant.
@@ -3283,19 +3283,19 @@ ERROR: unsupported quantifier "+!"
LINE 6: PATTERN (A+!)
^
HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions.
--- none of the following 4 queries should be accepted
+-- none of the following queries should be accepted
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ !) DEFINE A AS TRUE);
-ERROR: invalid token after "+" quantifier
+ERROR: invalid token "!" after "+" quantifier
LINE 1: ...S BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ !) DEFINE ...
^
HINT: Did you mean "+?" for reluctant quantifier?
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ ?+) DEFINE A AS TRUE);
-ERROR: invalid token after "+" quantifier
+ERROR: invalid token "?+" after "+" quantifier
LINE 1: ...S BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ ?+) DEFINE...
^
HINT: Did you mean "+?" for reluctant quantifier?
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A* ?+) DEFINE A AS TRUE);
-ERROR: invalid token after "*" quantifier
+ERROR: invalid token "?+" after "*" quantifier
LINE 1: ...S BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A* ?+) DEFINE...
^
HINT: Did you mean "*?" for reluctant quantifier?
@@ -3304,24 +3304,29 @@ ERROR: invalid quantifier combination
LINE 1: ...OWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A? ??) DEFI...
^
HINT: Did you mean "??" for reluctant quantifier?
+SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A {1,2}??) DEFINE A AS TRUE);
+ERROR: invalid token "??" after range quantifier
+LINE 1: ...TWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A {1,2}??) DEFINE...
+ ^
+HINT: Only "?" is allowed after {n,m} to make it reluctant.
-- none of the following 4 range-quantifier queries should be accepted
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2} !) DEFINE A AS TRUE);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "!" after range quantifier
LINE 1: ...BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2} !) DEFINE ...
^
HINT: Only "?" is allowed after {n} to make it reluctant.
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2,} !) DEFINE A AS TRUE);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "!" after range quantifier
LINE 1: ...ETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2,} !) DEFINE ...
^
HINT: Only "?" is allowed after {n,} or {,m} to make it reluctant.
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{,3} !) DEFINE A AS TRUE);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "!" after range quantifier
LINE 1: ...ETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{,3} !) DEFINE ...
^
HINT: Only "?" is allowed after {n,} or {,m} to make it reluctant.
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2,3} !) DEFINE A AS TRUE);
-ERROR: invalid token after range quantifier
+ERROR: invalid token "!" after range quantifier
LINE 1: ...TWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2,3} !) DEFINE ...
^
HINT: Only "?" is allowed after {n,m} to make it reluctant.
diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql
index cec7ea1e8db..5e95859f758 100644
--- a/src/test/regress/sql/rpr_base.sql
+++ b/src/test/regress/sql/rpr_base.sql
@@ -2146,11 +2146,12 @@ WINDOW w AS (
DEFINE A AS val > 0
);
--- none of the following 4 queries should be accepted
+-- none of the following queries should be accepted
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ !) DEFINE A AS TRUE);
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A+ ?+) DEFINE A AS TRUE);
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A* ?+) DEFINE A AS TRUE);
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A? ??) DEFINE A AS TRUE);
+SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A {1,2}??) DEFINE A AS TRUE);
-- none of the following 4 range-quantifier queries should be accepted
SELECT FROM rpr_err WINDOW w AS ( ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A{2} !) DEFINE A AS TRUE);
--
2.50.1 (Apple Git-155)