mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-28 09:54:15 +00:00
Merge pull request #77 from benesch/count-distinct
Support COUNT(DISTINCT x) and similar
This commit is contained in:
commit
646479e56c
3 changed files with 65 additions and 4 deletions
|
@ -112,6 +112,8 @@ pub enum ASTNode {
|
||||||
name: SQLObjectName,
|
name: SQLObjectName,
|
||||||
args: Vec<ASTNode>,
|
args: Vec<ASTNode>,
|
||||||
over: Option<SQLWindowSpec>,
|
over: Option<SQLWindowSpec>,
|
||||||
|
// aggregate functions may specify eg `COUNT(DISTINCT x)`
|
||||||
|
distinct: bool,
|
||||||
},
|
},
|
||||||
/// CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END
|
/// CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END
|
||||||
/// Note we only recognize a complete single expression as <condition>, not
|
/// Note we only recognize a complete single expression as <condition>, not
|
||||||
|
@ -190,8 +192,18 @@ impl ToString for ASTNode {
|
||||||
format!("{} {}", operator.to_string(), expr.as_ref().to_string())
|
format!("{} {}", operator.to_string(), expr.as_ref().to_string())
|
||||||
}
|
}
|
||||||
ASTNode::SQLValue(v) => v.to_string(),
|
ASTNode::SQLValue(v) => v.to_string(),
|
||||||
ASTNode::SQLFunction { name, args, over } => {
|
ASTNode::SQLFunction {
|
||||||
let mut s = format!("{}({})", name.to_string(), comma_separated_string(args));
|
name,
|
||||||
|
args,
|
||||||
|
over,
|
||||||
|
distinct,
|
||||||
|
} => {
|
||||||
|
let mut s = format!(
|
||||||
|
"{}({}{})",
|
||||||
|
name.to_string(),
|
||||||
|
if *distinct { "DISTINCT " } else { "" },
|
||||||
|
comma_separated_string(args)
|
||||||
|
);
|
||||||
if let Some(o) = over {
|
if let Some(o) = over {
|
||||||
s += &format!(" OVER ({})", o.to_string())
|
s += &format!(" OVER ({})", o.to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,6 +270,14 @@ impl Parser {
|
||||||
|
|
||||||
pub fn parse_function(&mut self, name: SQLObjectName) -> Result<ASTNode, ParserError> {
|
pub fn parse_function(&mut self, name: SQLObjectName) -> Result<ASTNode, ParserError> {
|
||||||
self.expect_token(&Token::LParen)?;
|
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 args = self.parse_optional_args()?;
|
let args = self.parse_optional_args()?;
|
||||||
let over = if self.parse_keyword("OVER") {
|
let over = if self.parse_keyword("OVER") {
|
||||||
// TBD: support window names (`OVER mywin`) in place of inline specification
|
// TBD: support window names (`OVER mywin`) in place of inline specification
|
||||||
|
@ -296,7 +304,12 @@ impl Parser {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ASTNode::SQLFunction { name, args, over })
|
Ok(ASTNode::SQLFunction {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
over,
|
||||||
|
distinct,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_window_frame(&mut self) -> Result<Option<SQLWindowFrame>, ParserError> {
|
pub fn parse_window_frame(&mut self) -> Result<Option<SQLWindowFrame>, ParserError> {
|
||||||
|
|
|
@ -211,11 +211,44 @@ fn parse_select_count_wildcard() {
|
||||||
name: SQLObjectName(vec!["COUNT".to_string()]),
|
name: SQLObjectName(vec!["COUNT".to_string()]),
|
||||||
args: vec![ASTNode::SQLWildcard],
|
args: vec![ASTNode::SQLWildcard],
|
||||||
over: None,
|
over: None,
|
||||||
|
distinct: false,
|
||||||
},
|
},
|
||||||
expr_from_projection(only(&select.projection))
|
expr_from_projection(only(&select.projection))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_select_count_distinct() {
|
||||||
|
let sql = "SELECT COUNT(DISTINCT + x) FROM customer";
|
||||||
|
let select = verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
&ASTNode::SQLFunction {
|
||||||
|
name: SQLObjectName(vec!["COUNT".to_string()]),
|
||||||
|
args: vec![ASTNode::SQLUnary {
|
||||||
|
operator: SQLOperator::Plus,
|
||||||
|
expr: Box::new(ASTNode::SQLIdentifier("x".to_string()))
|
||||||
|
}],
|
||||||
|
over: None,
|
||||||
|
distinct: true,
|
||||||
|
},
|
||||||
|
expr_from_projection(only(&select.projection))
|
||||||
|
);
|
||||||
|
|
||||||
|
one_statement_parses_to(
|
||||||
|
"SELECT COUNT(ALL + x) FROM customer",
|
||||||
|
"SELECT COUNT(+ x) FROM customer",
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT COUNT(ALL DISTINCT + x) FROM customer";
|
||||||
|
let res = parse_sql_statements(sql);
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError(
|
||||||
|
"Cannot specify both ALL and DISTINCT in function: COUNT".to_string()
|
||||||
|
),
|
||||||
|
res.unwrap_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_not() {
|
fn parse_not() {
|
||||||
let sql = "SELECT id FROM customer WHERE NOT salary = ''";
|
let sql = "SELECT id FROM customer WHERE NOT salary = ''";
|
||||||
|
@ -676,6 +709,7 @@ fn parse_scalar_function_in_projection() {
|
||||||
name: SQLObjectName(vec!["sqrt".to_string()]),
|
name: SQLObjectName(vec!["sqrt".to_string()]),
|
||||||
args: vec![ASTNode::SQLIdentifier("id".to_string())],
|
args: vec![ASTNode::SQLIdentifier("id".to_string())],
|
||||||
over: None,
|
over: None,
|
||||||
|
distinct: false,
|
||||||
},
|
},
|
||||||
expr_from_projection(only(&select.projection))
|
expr_from_projection(only(&select.projection))
|
||||||
);
|
);
|
||||||
|
@ -704,7 +738,8 @@ fn parse_window_functions() {
|
||||||
asc: Some(false)
|
asc: Some(false)
|
||||||
}],
|
}],
|
||||||
window_frame: None,
|
window_frame: None,
|
||||||
})
|
}),
|
||||||
|
distinct: false,
|
||||||
},
|
},
|
||||||
expr_from_projection(&select.projection[0])
|
expr_from_projection(&select.projection[0])
|
||||||
);
|
);
|
||||||
|
@ -776,6 +811,7 @@ fn parse_delimited_identifiers() {
|
||||||
name: SQLObjectName(vec![r#""myfun""#.to_string()]),
|
name: SQLObjectName(vec![r#""myfun""#.to_string()]),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
over: None,
|
over: None,
|
||||||
|
distinct: false,
|
||||||
},
|
},
|
||||||
expr_from_projection(&select.projection[1]),
|
expr_from_projection(&select.projection[1]),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue