From 576ecaad4451b926e9fb93779c65c19b60523f24 Mon Sep 17 00:00:00 2001 From: Henson Choi Date: Mon, 10 Aug 2026 15:52:43 +0900 Subject: [PATCH] Point the RPR quantifier diagnostics at the offending token A quantifier written as two operator tokens was reported by concatenating them, spelling out a quantifier nobody typed: "A *? ?" said invalid quantifier combination: "*??", and "A ?? || B" said "??||". Name the offending token instead, say which quantifier it follows, and point the cursor at it, the way the "*", "+" and range rules already do. When the first token spells no quantifier at all it is itself the offender, so report it the way the single-Op rule does: "A ?+ ?" and "A ?+ *" now agree on "?+". The lexer glues a trailing alternation operator onto a quantifier token, and rpr_invalid_quantifier_token() drops one before the token is quoted, so "A **|B" reports "**" like the spaced "A ** |B". With another '|' left over there is no quantifier to uncover, so "A ||B" and "A *||B" are quoted whole. The single-Op rule uses the helper too, which is what makes the glued and spaced spellings agree. The token an offender follows is quoted as typed, bar and all: stripping there would name a quantifier that legally accepts what comes next, as "A *| ?" and the accepted "A* ?" show. Two other cursors sat on a valid token. A range quantifier with a bad maximum pointed at the minimum, so "A{1,0}" blamed the 1; the bounds are now checked one at a time, with the messages the single-bound rules use. A dangling alternation operator pointed at the start of the pattern, so "A B ?|" blamed A; it now points at the element the operator hangs off. The hints listed *?, +? and ?? and then offered "their reluctant versions", which reads as sanctioning "*??" -- a spelling these very errors reject. They now list the plain quantifiers and say a "?" may follow each one, the way select.sgml describes them. --- src/backend/parser/gram.y | 83 +++++++++++++---- src/test/regress/expected/rpr_base.out | 121 ++++++++++++++++++++----- src/test/regress/sql/rpr_base.sql | 57 ++++++++++++ 3 files changed, 218 insertions(+), 43 deletions(-) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9b7a518295f..fdd0f2f4ec3 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -214,6 +214,7 @@ static RPRPatternNode *splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t y static RPRPatternNode *makeRPRQuantifier(int32 min, int32 max, bool reluctant, int location); static const char *rpr_invalid_quantifier_token(const char *tok); +static bool rpr_is_quantifier_token(const char *tok); %} @@ -17878,8 +17879,8 @@ row_pattern_quantifier_opt: else ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("unsupported quantifier \"%s\"", $1), - errhint("Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions."), + 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)); } /* RELUCTANT quantifiers (when lexer separates tokens) */ @@ -17919,12 +17920,19 @@ row_pattern_quantifier_opt: } | Op Op { - if (strcmp($1, "?") != 0) + if (!rpr_is_quantifier_token($1)) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid quantifier combination: \"%s%s\"", $1, $2), - errhint("Did you mean \"??\" for reluctant quantifier?"), + 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)); + if (strcmp($1, "?") != 0) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid token \"%s\" after \"%s\" quantifier", + rpr_invalid_quantifier_token($2), $1), + errhint("Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by \"?\" for the reluctant version."), + parser_errposition(@2)); if (strcmp($2, "?") == 0) $$ = (Node *) makeRPRQuantifier(0, 1, true, @1); else if (strcmp($2, "?|") == 0) @@ -17936,9 +17944,9 @@ row_pattern_quantifier_opt: else ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("invalid quantifier combination"), - errhint("Did you mean \"??\" for reluctant quantifier?"), - parser_errposition(@1)); + errmsg("invalid token \"%s\" after \"?\" quantifier", rpr_invalid_quantifier_token($2)), + errhint("Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by \"?\" for the reluctant version."), + parser_errposition(@2)); } /* {n}, {n,}, {,m}, {n,m} quantifiers */ | '{' Iconst '}' @@ -17970,11 +17978,16 @@ row_pattern_quantifier_opt: } | '{' Iconst ',' Iconst '}' { - if ($2 < 0 || $4 <= 0 || $2 >= RPR_QUANTITY_INF || $4 >= RPR_QUANTITY_INF) + if ($2 < 0 || $2 >= RPR_QUANTITY_INF) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("quantifier bounds must be between 0 and %d with max >= 1", RPR_QUANTITY_INF - 1), + errmsg("quantifier bound must be between 0 and %d", RPR_QUANTITY_INF - 1), parser_errposition(@2)); + if ($4 <= 0 || $4 >= RPR_QUANTITY_INF) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1), + parser_errposition(@4)); if ($2 > $4) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), @@ -18042,11 +18055,16 @@ row_pattern_quantifier_opt: 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 >= RPR_QUANTITY_INF || $4 >= RPR_QUANTITY_INF) + if ($2 < 0 || $2 >= RPR_QUANTITY_INF) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), - errmsg("quantifier bounds must be between 0 and %d with max >= 1", RPR_QUANTITY_INF - 1), + errmsg("quantifier bound must be between 0 and %d", RPR_QUANTITY_INF - 1), parser_errposition(@2)); + if ($4 <= 0 || $4 >= RPR_QUANTITY_INF) + ereport(ERROR, + errcode(ERRCODE_SYNTAX_ERROR), + errmsg("quantifier bound must be between 1 and %d", RPR_QUANTITY_INF - 1), + parser_errposition(@4)); if ($2 > $4) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), @@ -21555,7 +21573,7 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner) ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), errmsg("alternation operator \"|\" requires a pattern on both sides"), - parser_errposition(node->location)); + parser_errposition(child->location)); /* the right branch starts at its own first element, not the seq start */ rightnode = splitRPRTrailingAlt(makeRPRSeqOrSingle(righthalf, @@ -21577,25 +21595,50 @@ splitRPRTrailingAlt(RPRPatternNode *node, core_yyscan_t yyscanner) /* * rpr_invalid_quantifier_token - * Return the offending part of an invalid token following a quantifier. + * Return the offending part of an invalid token in a quantifier position. * * 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. + * single token (for example "*|"). Drop that trailing '|': it is the + * alternation operator, not part of the offending quantifier, so "*|" reports + * '*' and "*?|" reports "*?", exactly as the spaced spellings "* |" and "*? |" + * do. Only a single trailing operator is dropped: with another '|' left over + * there is no quantifier to uncover, so "||" and "*||" are reported whole, as + * are tokens with no trailing '|' such as "??" or "?+". */ static const char * rpr_invalid_quantifier_token(const char *tok) { size_t len = strlen(tok); - if (len > 1 && tok[len - 1] == '|') + if (len > 1 && tok[len - 1] == '|' && memchr(tok, '|', len - 1) == NULL) return pnstrdup(tok, len - 1); return tok; } +/* + * rpr_is_quantifier_token + * Does this Op token spell a quantifier? + * + * These are exactly the tokens the single-Op arm of row_pattern_quantifier_opt + * accepts, so the two must be kept in step. A token outside the set is not a + * quantifier at all and has to be reported as an unsupported one, the way that + * arm reports it, rather than as something a quantifier was followed by. + */ +static bool +rpr_is_quantifier_token(const char *tok) +{ + static const char *const quantifiers[] = { + "?", "*?", "+?", "??", "*|", "+|", "?|", "*?|", "+?|", "??|" + }; + + for (int i = 0; i < lengthof(quantifiers); i++) + { + if (strcmp(tok, quantifiers[i]) == 0) + return true; + } + return false; +} + /* 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 08e16486d98..2aa2c8b0992 100644 --- a/src/test/regress/expected/rpr_base.out +++ b/src/test/regress/expected/rpr_base.out @@ -1100,9 +1100,9 @@ WINDOW w AS ( DEFINE A AS val > 1000, B AS val > 0 ) ORDER BY id; -ERROR: quantifier bounds must be between 0 and 2147483646 with max >= 1 +ERROR: quantifier bound must be between 1 and 2147483646 LINE 6: PATTERN (A{0,0} B) - ^ + ^ -- {0,1} (equivalent to ?) SELECT id, val, COUNT(*) OVER w as cnt FROM rpr_quant @@ -1492,9 +1492,9 @@ WINDOW w AS ( PATTERN (A{1,2147483647}?) DEFINE A AS val > 0 ); -ERROR: quantifier bounds must be between 0 and 2147483646 with max >= 1 +ERROR: quantifier bound must be between 1 and 2147483646 LINE 6: PATTERN (A{1,2147483647}?) - ^ + ^ -- ERROR: {5,3}? (min > max is not allowed) SELECT COUNT(*) OVER w FROM rpr_reluctant @@ -1601,6 +1601,76 @@ WINDOW w AS ( 0 (3 rows) +-- Two operator tokens where the first is not "?": the offending one is the +-- second, so that is what gets named and what the cursor points at +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: 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 +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: 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. +-- 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 +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?+ ?) + DEFINE A AS val > 0 +); +ERROR: unsupported quantifier "?+" +LINE 6: PATTERN (A ?+ ?) + ^ +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. +-- The two tokens are reported separately, so the report cannot glue them into +-- a spelling that was never typed +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?? || B) + DEFINE A AS val > 0, B AS val > 1 +); +ERROR: invalid token "||" after "??" quantifier +LINE 6: PATTERN (A ?? || B) + ^ +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. +-- The trailing "|" belongs to the alternation, not to the quantifier, so the +-- report drops it +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?? ?|B) + DEFINE A AS val > 0, B AS val > 1 +); +ERROR: invalid token "?" after "??" quantifier +LINE 6: PATTERN (A ?? ?|B) + ^ +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. DROP TABLE rpr_reluctant; -- Quantifier boundary conditions CREATE TABLE rpr_bounds (id INT); @@ -4052,7 +4122,7 @@ SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRE ERROR: unsupported quantifier "&" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A&B) DEFINE... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. SELECT count(*) OVER w FROM rpr_glue 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 1: ...WEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*|) DEFIN... @@ -4061,16 +4131,21 @@ SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRE ERROR: alternation operator "|" requires a pattern on both sides LINE 1: ...WEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*| |B) DE... ^ +-- the dangling operator is blamed on the element it hangs off, not on the first +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: alternation operator "|" requires a pattern on both sides +LINE 1: ...EN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A B*|) DEFIN... + ^ 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: unsupported quantifier "*||" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*||B) DEFI... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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: unsupported quantifier "||" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A||B) DEFIN... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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: syntax error at or near ")" LINE 1: ...CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*|B|) DEFINE A... @@ -4094,10 +4169,10 @@ LINE 1: ...N CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A* *?|B) DEFI... ^ 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 quantifier combination -LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A? *?|B) DE... +ERROR: invalid token "*?" after "?" quantifier +LINE 1: ...N CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A? *?|B) DEFI... ^ -HINT: Did you mean "??" for reluctant quantifier? +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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 LINE 1: ... CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A{2}*?|B) DEFI... @@ -4111,25 +4186,25 @@ HINT: Only "?" is allowed after {n} to make it reluctant. -- Doubled op-char quantifiers lex as one Op token and are unsupported, whether -- glued to '|' ("**|", "*+|", "???|") or on their own ("**"). 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: unsupported quantifier "**|" +ERROR: unsupported quantifier "**" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A**|B) DEFI... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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: unsupported quantifier "*+|" +ERROR: unsupported quantifier "*+" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*+|B) DEFI... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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: unsupported quantifier "???|" +ERROR: unsupported quantifier "???" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A???|B) DEF... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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); ERROR: unsupported quantifier "**" LINE 1: ...EEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A**B) DEFIN... ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. DROP TABLE rpr_glue; -- ============================================================ -- Error Cases Tests @@ -4149,7 +4224,7 @@ WINDOW w AS ( ERROR: unsupported quantifier "+!" LINE 6: PATTERN (A+!) ^ -HINT: Valid quantifiers are: *, +, ?, *?, +?, ??, {n}, {n,}, {,m}, {n,m} and their reluctant versions. +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. -- 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 @@ -4167,10 +4242,10 @@ 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 quantifier combination -LINE 1: ...OWS BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A? ??) DEFI... +ERROR: invalid token "??" after "?" quantifier +LINE 1: ...S BETWEEN CURRENT ROW AND 1 FOLLOWING PATTERN (A? ??) DEFINE... ^ -HINT: Did you mean "??" for reluctant quantifier? +HINT: Valid quantifiers are: *, +, ?, {n}, {n,}, {,m}, {n,m}, each optionally followed by "?" for the reluctant version. 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... @@ -6777,9 +6852,9 @@ WINDOW w AS ( PATTERN ((A{2000000000,2147483647}){2}) DEFINE A AS val > 0 ); -ERROR: quantifier bounds must be between 0 and 2147483646 with max >= 1 +ERROR: quantifier bound must be between 1 and 2147483646 LINE 6: PATTERN ((A{2000000000,2147483647}){2}) - ^ + ^ -- Test: nested unbounded with large min causes overflow fallback EXPLAIN (COSTS OFF) SELECT COUNT(*) OVER w FROM rpr_fallback diff --git a/src/test/regress/sql/rpr_base.sql b/src/test/regress/sql/rpr_base.sql index cc9e8be89a4..28859538a48 100644 --- a/src/test/regress/sql/rpr_base.sql +++ b/src/test/regress/sql/rpr_base.sql @@ -1161,6 +1161,61 @@ WINDOW w AS ( DEFINE A AS val > 0 ); +-- Two operator tokens where the first is not "?": the offending one is the +-- second, so that is what gets named and what the cursor points at +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 +); + +-- 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 +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 +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?+ ?) + DEFINE A AS val > 0 +); + +-- The two tokens are reported separately, so the report cannot glue them into +-- a spelling that was never typed +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?? || B) + DEFINE A AS val > 0, B AS val > 1 +); + +-- The trailing "|" belongs to the alternation, not to the quantifier, so the +-- report drops it +SELECT COUNT(*) OVER w +FROM rpr_reluctant +WINDOW w AS ( + ORDER BY id + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + PATTERN (A ?? ?|B) + DEFINE A AS val > 0, B AS val > 1 +); + DROP TABLE rpr_reluctant; -- Quantifier boundary conditions @@ -2629,6 +2684,8 @@ ORDER BY id; 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); SELECT count(*) OVER w FROM rpr_glue WINDOW w AS (ORDER BY id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING PATTERN (A*|) DEFINE A AS val > 0); 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); +-- the dangling operator is blamed on the element it hangs off, not on the first +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); 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); 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); 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);