Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 39 additions & 36 deletions crates/squawk_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4333,41 +4333,48 @@ fn group_by_list(p: &mut Parser<'_>) {
// an arbitrary expression formed from input-column values. In case of
// ambiguity, a GROUP BY name will be interpreted as an input-column name
// rather than an output column name.

while !p.at(EOF) && !p.at(SEMICOLON) {
group_by_item(p);
if opt_group_by_item(p).is_none() {
p.error("expected group by item");
}
if !p.eat(COMMA) {
break;
}
}
}

fn group_by_item(p: &mut Parser<'_>) {
const GROUP_BY_ITEM_FIRST: TokenSet =
TokenSet::new(&[ROLLUP_KW, CUBE_KW, GROUPING_KW]).union(EXPR_FIRST);

fn opt_group_by_item(p: &mut Parser<'_>) -> Option<CompletedMarker> {
if !p.at_ts(GROUP_BY_ITEM_FIRST) {
return None;
}
let m = p.start();
let kind = match p.current() {
ROLLUP_KW => {
p.bump_any();
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expression list");
};
p.expect(R_PAREN);
paren_expr_list(p);
GROUPING_ROLLUP
}
CUBE_KW => {
p.bump_any();
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expression list");
};
p.expect(R_PAREN);
paren_expr_list(p);
GROUPING_CUBE
}
GROUPING_KW if p.nth_at(1, SETS_KW) => {
p.bump(GROUPING_KW);
p.bump(SETS_KW);
p.expect(L_PAREN);
group_by_list(p);
p.expect(R_PAREN);
delimited(
p,
L_PAREN,
R_PAREN,
COMMA,
|| "unexpected comma".to_string(),
GROUP_BY_ITEM_FIRST,
|p| opt_group_by_item(p).is_some(),
);
GROUPING_SETS
}
_ => {
Expand All @@ -4377,7 +4384,7 @@ fn group_by_item(p: &mut Parser<'_>) {
GROUPING_EXPR
}
};
m.complete(p, kind);
Some(m.complete(p, kind))
}

/// <https://www.postgresql.org/docs/17/sql-select.html#SQL-HAVING>
Expand Down Expand Up @@ -4575,18 +4582,26 @@ fn opt_all_or_distinct(p: &mut Parser) {
let m = p.start();
if p.eat(DISTINCT_KW) {
if p.eat(ON_KW) {
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expression in paren_expr_list");
}
p.expect(R_PAREN);
paren_expr_list(p);
}
m.complete(p, DISTINCT_CLAUSE);
} else {
m.abandon(p);
}
}

fn paren_expr_list(p: &mut Parser<'_>) {
delimited(
p,
L_PAREN,
R_PAREN,
COMMA,
|| "unexpected comma".to_string(),
EXPR_FIRST,
|p| opt_expr(p).is_some(),
);
}

/// All keywords
const COL_LABEL_FIRST: TokenSet = TokenSet::new(&[IDENT])
.union(UNRESERVED_KEYWORDS)
Expand Down Expand Up @@ -4908,25 +4923,13 @@ fn partition_option(p: &mut Parser<'_>) {
PARTITION_FOR_VALUES_WITH
// FOR VALUES IN '(' expr_list ')'
} else if p.eat(IN_KW) {
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expr list");
}
p.expect(R_PAREN);
paren_expr_list(p);
PARTITION_FOR_VALUES_IN
// FOR VALUES FROM '(' expr_list ')' TO '(' expr_list ')'
} else if p.eat(FROM_KW) {
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expr list");
}
p.expect(R_PAREN);
paren_expr_list(p);
p.expect(TO_KW);
p.expect(L_PAREN);
if !expr_list(p) {
p.error("expected expr list");
}
p.expect(R_PAREN);
paren_expr_list(p);
PARTITION_FOR_VALUES_FROM
} else {
p.error("expected partition option");
Expand Down
8 changes: 8 additions & 0 deletions crates/squawk_parser/tests/data/err/create_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ create unlogged table t (
)
);

create table t partition of u
-- missing some commas
for values from ('2024-01-01' 1) to ('2024-04-01' 5);

create table t partition of u
-- missing some commas
for values in (1 2 3);

-- exclude missing a comma
create table t (
a int,
Expand Down
11 changes: 11 additions & 0 deletions crates/squawk_parser/tests/data/err/select.sql
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ select a, b c d, e from t;
-- ^ ^ comma missing
-- \-- this is a label

-- distinct on missing a comma
SELECT DISTINCT ON (a b) a, b, c
FROM t
order by a, b desc;

-- group bys with missing commas
select * from t group by rollup (1 2 3);
select * from t group by cube (1 2 3);
select * from u
group by grouping sets((1 2) grouping sets((), grouping sets(())));

-- trailing comma in args
select f(1,);

Expand Down
97 changes: 95 additions & 2 deletions crates/squawk_parser/tests/snapshots/tests__create_table_err.snap
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,95 @@ SOURCE_FILE
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n\n"
CREATE_TABLE
CREATE_KW "create"
WHITESPACE " "
TABLE_KW "table"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME
IDENT "t"
WHITESPACE " "
PARTITION_OF
PARTITION_KW "partition"
WHITESPACE " "
OF_KW "of"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME_REF
IDENT "u"
WHITESPACE "\n "
COMMENT "-- missing some commas"
WHITESPACE "\n "
PARTITION_FOR_VALUES_FROM
FOR_KW "for"
WHITESPACE " "
VALUES_KW "values"
WHITESPACE " "
FROM_KW "from"
WHITESPACE " "
L_PAREN "("
LITERAL
STRING "'2024-01-01'"
WHITESPACE " "
LITERAL
INT_NUMBER "1"
R_PAREN ")"
WHITESPACE " "
TO_KW "to"
WHITESPACE " "
L_PAREN "("
LITERAL
STRING "'2024-04-01'"
WHITESPACE " "
LITERAL
INT_NUMBER "5"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n\n"
CREATE_TABLE
CREATE_KW "create"
WHITESPACE " "
TABLE_KW "table"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME
IDENT "t"
WHITESPACE " "
PARTITION_OF
PARTITION_KW "partition"
WHITESPACE " "
OF_KW "of"
WHITESPACE " "
PATH
PATH_SEGMENT
NAME_REF
IDENT "u"
WHITESPACE "\n "
COMMENT "-- missing some commas"
WHITESPACE "\n "
PARTITION_FOR_VALUES_IN
FOR_KW "for"
WHITESPACE " "
VALUES_KW "values"
WHITESPACE " "
IN_KW "in"
WHITESPACE " "
L_PAREN "("
LITERAL
INT_NUMBER "1"
WHITESPACE " "
LITERAL
INT_NUMBER "2"
WHITESPACE " "
LITERAL
INT_NUMBER "3"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n\n"
CREATE_TABLE
COMMENT "-- exclude missing a comma"
WHITESPACE "\n"
Expand Down Expand Up @@ -571,5 +660,9 @@ ERROR@198: unexpected comma
ERROR@199: unexpected comma
ERROR@200: unexpected comma
ERROR@201: unexpected comma
ERROR@1021: expected COMMA
ERROR@1063: expected SEMICOLON
ERROR@1011: expected COMMA
ERROR@1032: expected COMMA
ERROR@1116: expected COMMA
ERROR@1119: expected COMMA
ERROR@1226: expected COMMA
ERROR@1268: expected SEMICOLON
Loading
Loading