Skip to content

Commit fd59b5a

Browse files
committed
parse grouping sets, rollup, and cube
1 parent 9c87d3b commit fd59b5a

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

src/ast/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ pub enum Expr {
278278
Subquery(Box<Query>),
279279
/// The `LISTAGG` function `SELECT LISTAGG(...) WITHIN GROUP (ORDER BY ...)`
280280
ListAgg(ListAgg),
281+
/// The `GROUPING SETS` expr.
282+
GroupingSets(Vec<Vec<Expr>>),
283+
/// The `CUBE` expr.
284+
Cube(Vec<Vec<Expr>>),
285+
/// The `ROLLUP` expr.
286+
Rollup(Vec<Vec<Expr>>),
281287
}
282288

283289
impl fmt::Display for Expr {

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ define_keywords!(
401401
SESSION,
402402
SESSION_USER,
403403
SET,
404+
SETS,
404405
SHOW,
405406
SIMILAR,
406407
SMALLINT,

src/parser.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ impl<'a> Parser<'a> {
336336
return_ok_if_some!(self.maybe_parse(|parser| {
337337
match parser.parse_data_type()? {
338338
DataType::Interval => parser.parse_literal_interval(),
339-
// PosgreSQL allows almost any identifier to be used as custom data type name,
339+
// PostgreSQL allows almost any identifier to be used as custom data type name,
340340
// and we support that in `parse_data_type()`. But unlike Postgres we don't
341341
// have a list of globally reserved keywords (since they vary across dialects),
342342
// so given `NOT 'a' LIKE 'b'`, we'd accept `NOT` as a possible custom data type
@@ -557,6 +557,48 @@ impl<'a> Parser<'a> {
557557
}
558558
}
559559

560+
/// parse a group by expr. a group by expr can be one of group sets, roll up, cube, or simple
561+
/// expr.
562+
fn parse_group_by_expr(&mut self) -> Result<Expr, ParserError> {
563+
if self.parse_keywords(&[Keyword::GROUPING, Keyword::SETS]) {
564+
self.expect_token(&Token::LParen)?;
565+
let result = self.parse_comma_separated(|p| p.parse_tuple(false))?;
566+
self.expect_token(&Token::RParen)?;
567+
Ok(Expr::GroupingSets(result))
568+
} else if self.parse_keyword(Keyword::CUBE) {
569+
self.expect_token(&Token::LParen)?;
570+
let result = self.parse_comma_separated(|p| p.parse_tuple(true))?;
571+
self.expect_token(&Token::RParen)?;
572+
Ok(Expr::Cube(result))
573+
} else if self.parse_keyword(Keyword::ROLLUP) {
574+
self.expect_token(&Token::LParen)?;
575+
let result = self.parse_comma_separated(|p| p.parse_tuple(true))?;
576+
self.expect_token(&Token::RParen)?;
577+
Ok(Expr::Rollup(result))
578+
} else {
579+
self.parse_expr()
580+
}
581+
}
582+
583+
/// parse a tuple with `(` and `)`, allowing for empty tuples. If lift_singleton is true,
584+
/// then a singleton tuple is lifted to a tuple of length 1, otherwise it will fail.
585+
fn parse_tuple(&mut self, lift_singleton: bool) -> Result<Vec<Expr>, ParserError> {
586+
if lift_singleton {
587+
if self.consume_token(&Token::LParen) {
588+
let result = self.parse_comma_separated(Parser::parse_expr)?;
589+
self.expect_token(&Token::RParen)?;
590+
Ok(result)
591+
} else {
592+
Ok(vec![self.parse_expr()?])
593+
}
594+
} else {
595+
self.expect_token(&Token::LParen)?;
596+
let result = self.parse_comma_separated(Parser::parse_expr)?;
597+
self.expect_token(&Token::RParen)?;
598+
Ok(result)
599+
}
600+
}
601+
560602
pub fn parse_case_expr(&mut self) -> Result<Expr, ParserError> {
561603
let mut operand = None;
562604
if !self.parse_keyword(Keyword::WHEN) {
@@ -2492,7 +2534,7 @@ impl<'a> Parser<'a> {
24922534
};
24932535

24942536
let group_by = if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) {
2495-
self.parse_comma_separated(Parser::parse_expr)?
2537+
self.parse_comma_separated(Parser::parse_group_by_expr)?
24962538
} else {
24972539
vec![]
24982540
};

0 commit comments

Comments
 (0)