diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index 4985ec88..0f3763da 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -4504,12 +4504,12 @@ fn opt_group_by_clause(p: &mut Parser<'_>) -> Option { if p.at(ALL_KW) || p.at(DISTINCT_KW) { p.bump_any(); } - group_by_list(p); + opt_group_by_list(p); Some(m.complete(p, GROUP_BY_CLAUSE)) } -fn group_by_list(p: &mut Parser<'_>) { +fn opt_group_by_list(p: &mut Parser<'_>) { // From pg docs: // An expression used inside a grouping_element can be an input column name, // or the name or ordinal number of an output column (SELECT list item), or @@ -4518,15 +4518,21 @@ fn group_by_list(p: &mut Parser<'_>) { // rather than an output column name. let m = p.start(); - while !p.at(EOF) && !p.at(SEMICOLON) { + let mut found_item = false; + while !p.at(EOF) { if opt_group_by_item(p).is_none() { - p.error("expected group by item"); + break; } + found_item = true; if !p.eat(COMMA) { break; } } - m.complete(p, GROUP_BY_LIST); + if found_item { + m.complete(p, GROUP_BY_LIST); + } else { + m.abandon(p); + } } const GROUP_BY_ITEM_FIRST: TokenSet = diff --git a/crates/squawk_parser/tests/data/ok/select_cte_pg19.sql b/crates/squawk_parser/tests/data/ok/select_cte_pg19.sql new file mode 100644 index 00000000..d5ed3a86 --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/select_cte_pg19.sql @@ -0,0 +1,5 @@ +-- group by all +with x as ( + select * from t group by all +) +select * from x; diff --git a/crates/squawk_parser/tests/snapshots/tests__select_cte_pg19_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_cte_pg19_ok.snap new file mode 100644 index 00000000..c7533e6a --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__select_cte_pg19_ok.snap @@ -0,0 +1,58 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/select_cte_pg19.sql +--- +SOURCE_FILE + COMMENT "-- group by all" + WHITESPACE "\n" + SELECT + WITH_CLAUSE + WITH_KW "with" + WHITESPACE " " + WITH_TABLE + NAME + IDENT "x" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + NAME_REF + IDENT "t" + WHITESPACE " " + GROUP_BY_CLAUSE + GROUP_KW "group" + WHITESPACE " " + BY_KW "by" + WHITESPACE " " + ALL_KW "all" + WHITESPACE "\n" + R_PAREN ")" + WHITESPACE "\n" + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + NAME_REF + IDENT "x" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/tests.rs b/crates/squawk_parser/tests/tests.rs index 9a6bc98c..71f6f951 100644 --- a/crates/squawk_parser/tests/tests.rs +++ b/crates/squawk_parser/tests/tests.rs @@ -34,8 +34,9 @@ fn parser_ok(fixture: Fixture<&str>) { errors.is_none(), "tests defined in the `ok` can't have parser errors." ); - // skipping pg17/pg18 specific stuff since our parser isn't using the latest parser - if !test_name.ends_with("pg17") && !test_name.ends_with("pg18") { + // skipping pg17/pg18/pg19 specific stuff since our parser isn't using the latest parser + if !test_name.ends_with("pg17") && !test_name.ends_with("pg18") && !test_name.ends_with("pg19") + { let pg_result = pg_query::parse(content); if let Err(e) = &pg_result { assert!(