-
Notifications
You must be signed in to change notification settings - Fork 612
provide LISTAGG implementation #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6896953
c6216ec
48d6a8b
eba849a
a95fa55
ee144b2
1e41d30
1e952b6
5736b9d
bcc41a3
a16c3a4
e1ad011
158c650
b2cb822
b504072
7e53d37
d1ff825
87ac2a1
95c5312
4e17b47
7a25837
0596074
f1f8c4a
5964599
ebe00d7
815854f
2b7d116
f92443b
cd471e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,6 +191,7 @@ impl Parser { | |
"EXISTS" => self.parse_exists_expr(), | ||
"EXTRACT" => self.parse_extract_expr(), | ||
"INTERVAL" => self.parse_literal_interval(), | ||
"LISTAGG" => self.parse_listagg_expr(), | ||
"NOT" => Ok(Expr::UnaryOp { | ||
op: UnaryOperator::Not, | ||
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), | ||
|
@@ -272,14 +273,7 @@ impl Parser { | |
|
||
pub fn parse_function(&mut self, name: ObjectName) -> Result<Expr, ParserError> { | ||
self.expect_token(&Token::LParen)?; | ||
let all = self.parse_keyword("ALL"); | ||
let distinct = self.parse_keyword("DISTINCT"); | ||
if all && distinct { | ||
return parser_err!(format!( | ||
"Cannot specify both ALL and DISTINCT in function: {}", | ||
name.to_string(), | ||
)); | ||
} | ||
let distinct = self.parse_all_or_distinct()?; | ||
let args = self.parse_optional_args()?; | ||
let over = if self.parse_keyword("OVER") { | ||
// TBD: support window names (`OVER mywin`) in place of inline specification | ||
|
@@ -423,6 +417,66 @@ impl Parser { | |
}) | ||
} | ||
|
||
/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`. | ||
pub fn parse_listagg_expr(&mut self) -> Result<Expr, ParserError> { | ||
self.expect_token(&Token::LParen)?; | ||
let distinct = self.parse_all_or_distinct()?; | ||
let expr = Box::new(self.parse_expr()?); | ||
// While ANSI SQL would would require the separator, Redshift makes this optional. Here we | ||
// choose to make the separator optional as this provides the more general implementation. | ||
let separator = if self.consume_token(&Token::Comma) { | ||
Some(Box::new(self.parse_expr()?)) | ||
} else { | ||
None | ||
}; | ||
let on_overflow = if self.parse_keywords(vec!["ON", "OVERFLOW"]) { | ||
if self.parse_keyword("ERROR") { | ||
Some(ListAggOnOverflow::Error) | ||
} else { | ||
maxcountryman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.expect_keyword("TRUNCATE")?; | ||
let filler = match self.peek_token() { | ||
Some(Token::Word(kw)) if kw.keyword == "WITH" || kw.keyword == "WITHOUT" => { | ||
None | ||
} | ||
Some(Token::SingleQuotedString(_)) | ||
| Some(Token::NationalStringLiteral(_)) | ||
| Some(Token::HexStringLiteral(_)) => Some(Box::new(self.parse_expr()?)), | ||
Comment on lines
+441
to
+443
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows for any supported string literal to be parsed as a filler and uses Also it might be a good idea to be stricter with the |
||
_ => self.expected( | ||
"either filler, WITH, or WITHOUT in LISTAGG", | ||
self.peek_token(), | ||
)?, | ||
}; | ||
let with_count = self.parse_keyword("WITH"); | ||
if !with_count && !self.parse_keyword("WITHOUT") { | ||
self.expected("either WITH or WITHOUT in LISTAGG", self.peek_token())?; | ||
} | ||
self.expect_keyword("COUNT")?; | ||
Some(ListAggOnOverflow::Truncate { filler, with_count }) | ||
} | ||
} else { | ||
None | ||
}; | ||
self.expect_token(&Token::RParen)?; | ||
// Once again ANSI SQL requires WITHIN GROUP, but Redshift does not. Again we choose the | ||
// more general implementation. | ||
let within_group = if self.parse_keywords(vec!["WITHIN", "GROUP"]) { | ||
self.expect_token(&Token::LParen)?; | ||
self.expect_keywords(&["ORDER", "BY"])?; | ||
let order_by_expr = self.parse_comma_separated(Parser::parse_order_by_expr)?; | ||
self.expect_token(&Token::RParen)?; | ||
order_by_expr | ||
} else { | ||
vec![] | ||
}; | ||
Ok(Expr::ListAgg(ListAgg { | ||
distinct, | ||
expr, | ||
separator, | ||
on_overflow, | ||
within_group, | ||
})) | ||
} | ||
|
||
// This function parses date/time fields for both the EXTRACT function-like | ||
// operator and interval qualifiers. EXTRACT supports a wider set of | ||
// date/time fields than interval qualifiers, so this function may need to | ||
|
@@ -851,6 +905,18 @@ impl Parser { | |
Ok(values) | ||
} | ||
|
||
/// Parse either `ALL` or `DISTINCT`. Returns `true` if `DISTINCT` is parsed and results in a | ||
/// `ParserError` if both `ALL` and `DISTINCT` are fround. | ||
pub fn parse_all_or_distinct(&mut self) -> Result<bool, ParserError> { | ||
maxcountryman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let all = self.parse_keyword("ALL"); | ||
let distinct = self.parse_keyword("DISTINCT"); | ||
if all && distinct { | ||
return parser_err!("Cannot specify both ALL and DISTINCT".to_string()); | ||
nickolay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else { | ||
Ok(distinct) | ||
} | ||
} | ||
|
||
/// Parse a SQL CREATE statement | ||
pub fn parse_create(&mut self) -> Result<Statement, ParserError> { | ||
if self.parse_keyword("TABLE") { | ||
|
@@ -1635,11 +1701,7 @@ impl Parser { | |
/// Parse a restricted `SELECT` statement (no CTEs / `UNION` / `ORDER BY`), | ||
/// assuming the initial `SELECT` was already consumed | ||
pub fn parse_select(&mut self) -> Result<Select, ParserError> { | ||
let all = self.parse_keyword("ALL"); | ||
let distinct = self.parse_keyword("DISTINCT"); | ||
if all && distinct { | ||
return parser_err!("Cannot specify both ALL and DISTINCT in SELECT"); | ||
} | ||
let distinct = self.parse_all_or_distinct()?; | ||
|
||
let top = if self.parse_keyword("TOP") { | ||
Some(self.parse_top()?) | ||
|
Uh oh!
There was an error while loading. Please reload this page.