Skip to content

Commit eb3450d

Browse files
committed
Support HAVING without GROUP BY
...which is weird but allowed: https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#table-expression https://dba.stackexchange.com/a/57453/15599 Also add a test for GROUP BY .. HAVING
1 parent d60bdc0 commit eb3450d

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

src/dialect/keywords.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ define_keywords!(
420420
/// can be parsed unambiguously without looking ahead.
421421
pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[
422422
// Reserved as both a table and a column alias:
423-
WITH, SELECT, WHERE, GROUP, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
423+
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
424424
// Reserved only as a table alias in the `FROM`/`JOIN` clauses:
425425
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING,
426426
];
@@ -429,7 +429,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[
429429
/// can be parsed unambiguously without looking ahead.
430430
pub const RESERVED_FOR_COLUMN_ALIAS: &[&str] = &[
431431
// Reserved as both a table and a column alias:
432-
WITH, SELECT, WHERE, GROUP, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
432+
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
433433
// Reserved only as a column alias in the `SELECT` clause:
434434
FROM,
435435
];

src/sqlparser.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1566,6 +1566,11 @@ impl Parser {
15661566
}
15671567
let projection = self.parse_select_list()?;
15681568

1569+
// Note that for keywords to be properly handled here, they need to be
1570+
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
1571+
// otherwise they may be parsed as an alias as part of the `projection`
1572+
// or `from`.
1573+
15691574
let mut from = vec![];
15701575
if self.parse_keyword("FROM") {
15711576
loop {

tests/sqlparser_common.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,29 @@ fn parse_select_group_by() {
789789
);
790790
}
791791

792+
#[test]
793+
fn parse_select_having() {
794+
let sql = "SELECT foo FROM bar GROUP BY foo HAVING COUNT(*) > 1";
795+
let select = verified_only_select(sql);
796+
assert_eq!(
797+
Some(ASTNode::SQLBinaryOp {
798+
left: Box::new(ASTNode::SQLFunction(SQLFunction {
799+
name: SQLObjectName(vec!["COUNT".to_string()]),
800+
args: vec![ASTNode::SQLWildcard],
801+
over: None,
802+
distinct: false
803+
})),
804+
op: SQLBinaryOperator::Gt,
805+
right: Box::new(ASTNode::SQLValue(Value::Long(1)))
806+
}),
807+
select.having
808+
);
809+
810+
let sql = "SELECT 'foo' HAVING 1 = 1";
811+
let select = verified_only_select(sql);
812+
assert!(select.having.is_some());
813+
}
814+
792815
#[test]
793816
fn parse_limit_accepts_all() {
794817
one_statement_parses_to(

0 commit comments

Comments
 (0)