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:
Nickolay Ponomarev 2019-02-11 03:27:55 +03:00
parent c8e7c3b343
commit d4de248c73
4 changed files with 257 additions and 15 deletions

View file

@ -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> {