From 6ad627a8a482a8f6047971f48561edb432e79f72 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Sat, 29 Aug 2026 17:56:17 +0900 Subject: [PATCH] Blame the alternation when a glued quantifier token is followed by an Op The lexer glues a quantifier and a trailing alternation operator into one token, so "A*|B" arrives as Op "*|" and parses as "A* | B". When such a token was followed by another Op, the report named the pair: PATTERN (A *| ?) ERROR: invalid token "?" after "*|" quantifier "*|" is not a quantifier. Nor is "*" the answer: "A* ?" is accepted, so naming that pair would describe something the grammar takes. The token already carries the alternation operator, and what follows an alternation has to be a pattern -- an Op never is one. So report the alternation, in the shape the message already uses elsewhere: ERROR: alternation operator "|" requires a pattern on both sides Six tokens reach this arm: "*|", "+|", "?|", "*?|", "+?|" and "??|". All six now say the same thing, and none can reach the report that follows, so the quantifier that report names is always one the user wrote whole. Nothing else moves. An unglued pair such as "A *? ??" still reports the quantifier it was given, a valid "A *| B" still parses, and the set of accepted patterns is unchanged. rpr_base gains the glued two-character case, where naming a pair would have had to invent "*?" out of "*?|". --- src/backend/parser/gram.y | 11 +++++++++++ src/test/regress/expected/rpr_base.out | 22 ++++++++++++++++++---- src/test/regress/sql/rpr_base.sql | 17 +++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 6af4e7d92cd..efd32aae7be 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -17891,6 +17891,17 @@ row_pattern_quantifier_opt: errmsg("unsupported quantifier \"%s\"", rpr_invalid_quantifier_token($1)), errhint("Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by \"?\" for the reluctant version."), parser_errposition(@1)); + /* + * A first token ending in "|" carries the alternation + * operator, so what comes after it has to be a pattern. + * An Op never is one, and naming the pair would describe + * a quantifier the token only half spells. + */ + if (strchr($1, '|') != NULL) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("alternation operator \"|\" requires a pattern on both sides"), + parser_errposition(@2)); if (strcmp($1, "?") != 0) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/test/regress/expected/rpr_base.out b/src/test/regress/expected/rpr_base.out index 765291582c7..b8d66fad729 100644 --- a/src/test/regress/expected/rpr_base.out +++ b/src/test/regress/expected/rpr_base.out @@ -1640,8 +1640,10 @@ ERROR: invalid token "?" after "*?" quantifier LINE 6: PATTERN (A *? ?) ^ HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. --- The first token is quoted as typed: stripping its "|" would name "*", and --- "A* ?" is accepted, so the error would describe a pair the grammar takes +-- A first token ending in "|" is a quantifier plus the alternation operator, +-- so what follows it has to be a pattern and an Op never is one. The report +-- names the alternation rather than the pair: "A* ?" is accepted, so naming +-- that pair would describe something the grammar takes. SELECT COUNT(*) OVER w FROM rpr_reluctant WINDOW w AS ( @@ -1650,10 +1652,22 @@ WINDOW w AS ( PATTERN (A *| ?) DEFINE A AS val > 0 ); -ERROR: invalid token "?" after "*|" quantifier +ERROR: alternation operator "|" requires a pattern on both sides LINE 6: PATTERN (A *| ?) ^ -HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. +-- the same for a glued two-character quantifier, where naming the pair would +-- have to invent "*?" out of "*?|" +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A *?| ??) + DEFINE A AS val > 0 +); +ERROR: alternation operator "|" requires a pattern on both sides +LINE 6: PATTERN (A *?| ??) + ^ -- A first token that is no quantifier at all is itself the offending one, so it -- is reported the same way as when it stands alone SELECT COUNT(*) OVER w diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql index bccee6974bf..807577bfe52 100644 --- a/src/test/regress/sql/rpr_base.sql +++ b/src/test/regress/sql/rpr_base.sql @@ -1182,8 +1182,10 @@ WINDOW w AS ( DEFINE A AS val > 0 ); --- The first token is quoted as typed: stripping its "|" would name "*", and --- "A* ?" is accepted, so the error would describe a pair the grammar takes +-- A first token ending in "|" is a quantifier plus the alternation operator, +-- so what follows it has to be a pattern and an Op never is one. The report +-- names the alternation rather than the pair: "A* ?" is accepted, so naming +-- that pair would describe something the grammar takes. SELECT COUNT(*) OVER w FROM rpr_reluctant WINDOW w AS ( @@ -1193,6 +1195,17 @@ WINDOW w AS ( DEFINE A AS val > 0 ); +-- the same for a glued two-character quantifier, where naming the pair would +-- have to invent "*?" out of "*?|" +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A *?| ??) + DEFINE A AS val > 0 +); + -- A first token that is no quantifier at all is itself the offending one, so it -- is reported the same way as when it stands alone SELECT COUNT(*) OVER w