From 3d015812c7462a8fcfe02d2a34ebdcf773e2c23a Mon Sep 17 00:00:00 2001 From: Henson Choi 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)