mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-09 23:46:20 +00:00
Support OVER clause for window/analytic functions
Since this changes SQLFunction anyway, changed its `id` field to `name`, as we don't seem to use "id" to mean "name" anywhere else.
This commit is contained in:
parent
c8e7c3b343
commit
d4de248c73
4 changed files with 257 additions and 15 deletions
|
@ -237,7 +237,7 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse_function(&mut self, id: SQLIdent) -> Result<ASTNode, ParserError> {
|
||||
pub fn parse_function(&mut self, name: SQLIdent) -> Result<ASTNode, ParserError> {
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let args = if self.consume_token(&Token::RParen) {
|
||||
vec![]
|
||||
|
@ -246,7 +246,98 @@ impl Parser {
|
|||
self.expect_token(&Token::RParen)?;
|
||||
args
|
||||
};
|
||||
Ok(ASTNode::SQLFunction { id, args })
|
||||
let over = if self.parse_keyword("OVER") {
|
||||
// TBD: support window names (`OVER mywin`) in place of inline specification
|
||||
self.expect_token(&Token::LParen)?;
|
||||
let partition_by = if self.parse_keywords(vec!["PARTITION", "BY"]) {
|
||||
// a list of possibly-qualified column names
|
||||
self.parse_expr_list()?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let order_by = if self.parse_keywords(vec!["ORDER", "BY"]) {
|
||||
self.parse_order_by_expr_list()?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let window_frame = self.parse_window_frame()?;
|
||||
|
||||
Some(SQLWindowSpec {
|
||||
partition_by,
|
||||
order_by,
|
||||
window_frame,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(ASTNode::SQLFunction { name, args, over })
|
||||
}
|
||||
|
||||
pub fn parse_window_frame(&mut self) -> Result<Option<SQLWindowFrame>, ParserError> {
|
||||
let window_frame = match self.peek_token() {
|
||||
Some(Token::SQLWord(w)) => {
|
||||
let units = w.keyword.parse::<SQLWindowFrameUnits>()?;
|
||||
self.next_token();
|
||||
if self.parse_keyword("BETWEEN") {
|
||||
let start_bound = self.parse_window_frame_bound()?;
|
||||
self.expect_keyword("AND")?;
|
||||
let end_bound = Some(self.parse_window_frame_bound()?);
|
||||
Some(SQLWindowFrame {
|
||||
units,
|
||||
start_bound,
|
||||
end_bound,
|
||||
})
|
||||
} else {
|
||||
let start_bound = self.parse_window_frame_bound()?;
|
||||
let end_bound = None;
|
||||
Some(SQLWindowFrame {
|
||||
units,
|
||||
start_bound,
|
||||
end_bound,
|
||||
})
|
||||
}
|
||||
}
|
||||
Some(Token::RParen) => None,
|
||||
unexpected => {
|
||||
return parser_err!(format!(
|
||||
"Expected 'ROWS', 'RANGE', 'GROUPS', or ')', got {:?}",
|
||||
unexpected
|
||||
));
|
||||
}
|
||||
};
|
||||
self.expect_token(&Token::RParen)?;
|
||||
Ok(window_frame)
|
||||
}
|
||||
|
||||
/// "CURRENT ROW" | ( (<positive number> | "UNBOUNDED") ("PRECEDING" | FOLLOWING) )
|
||||
pub fn parse_window_frame_bound(&mut self) -> Result<SQLWindowFrameBound, ParserError> {
|
||||
if self.parse_keywords(vec!["CURRENT", "ROW"]) {
|
||||
Ok(SQLWindowFrameBound::CurrentRow)
|
||||
} else {
|
||||
let rows = if self.parse_keyword("UNBOUNDED") {
|
||||
None
|
||||
} else {
|
||||
let rows = self.parse_literal_int()?;
|
||||
if rows < 0 {
|
||||
parser_err!(format!(
|
||||
"The number of rows must be non-negative, got {}",
|
||||
rows
|
||||
))?;
|
||||
}
|
||||
Some(rows as u64)
|
||||
};
|
||||
if self.parse_keyword("PRECEDING") {
|
||||
Ok(SQLWindowFrameBound::Preceding(rows))
|
||||
} else if self.parse_keyword("FOLLOWING") {
|
||||
Ok(SQLWindowFrameBound::Following(rows))
|
||||
} else {
|
||||
parser_err!(format!(
|
||||
"Expected PRECEDING or FOLLOWING, found {:?}",
|
||||
self.peek_token()
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_case_expression(&mut self) -> Result<ASTNode, ParserError> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue