mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-09 08:48:02 +00:00
Fix interval parsing logic and precedence (#705)
* initial fix * add comma * add test * style * add more tests * codestyle fix
This commit is contained in:
parent
4b1dc1abf7
commit
57083a0df1
2 changed files with 146 additions and 1 deletions
|
@ -363,6 +363,36 @@ impl<'a> Parser<'a> {
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_interval_expr(&mut self) -> Result<Expr, ParserError> {
|
||||||
|
let precedence = 0;
|
||||||
|
let mut expr = self.parse_prefix()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let next_precedence = self.get_next_interval_precedence()?;
|
||||||
|
|
||||||
|
if precedence >= next_precedence {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr = self.parse_infix(expr, next_precedence)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the precedence of the next token
|
||||||
|
/// With AND, OR, and XOR
|
||||||
|
pub fn get_next_interval_precedence(&self) -> Result<u8, ParserError> {
|
||||||
|
let token = self.peek_token();
|
||||||
|
|
||||||
|
match token {
|
||||||
|
Token::Word(w) if w.keyword == Keyword::AND => Ok(0),
|
||||||
|
Token::Word(w) if w.keyword == Keyword::OR => Ok(0),
|
||||||
|
Token::Word(w) if w.keyword == Keyword::XOR => Ok(0),
|
||||||
|
_ => self.get_next_precedence(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_assert(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_assert(&mut self) -> Result<Statement, ParserError> {
|
||||||
let condition = self.parse_expr()?;
|
let condition = self.parse_expr()?;
|
||||||
let message = if self.parse_keyword(Keyword::AS) {
|
let message = if self.parse_keyword(Keyword::AS) {
|
||||||
|
@ -1200,7 +1230,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
// The first token in an interval is a string literal which specifies
|
// The first token in an interval is a string literal which specifies
|
||||||
// the duration of the interval.
|
// the duration of the interval.
|
||||||
let value = self.parse_expr()?;
|
let value = self.parse_interval_expr()?;
|
||||||
|
|
||||||
// Following the string literal is a qualifier which indicates the units
|
// Following the string literal is a qualifier which indicates the units
|
||||||
// of the duration specified in the string literal.
|
// of the duration specified in the string literal.
|
||||||
|
|
|
@ -3237,6 +3237,121 @@ fn parse_interval() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_interval_and_or_xor() {
|
||||||
|
let sql = "SELECT col FROM test \
|
||||||
|
WHERE d3_date > d1_date + INTERVAL '5 days' \
|
||||||
|
AND d2_date > d1_date + INTERVAL '3 days'";
|
||||||
|
|
||||||
|
let actual_ast = Parser::parse_sql(&GenericDialect {}, sql).unwrap();
|
||||||
|
|
||||||
|
let expected_ast = vec![Statement::Query(Box::new(Query {
|
||||||
|
with: None,
|
||||||
|
body: Box::new(SetExpr::Select(Box::new(Select {
|
||||||
|
distinct: false,
|
||||||
|
top: None,
|
||||||
|
projection: vec![UnnamedExpr(Expr::Identifier(Ident {
|
||||||
|
value: "col".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
}))],
|
||||||
|
into: None,
|
||||||
|
from: vec![TableWithJoins {
|
||||||
|
relation: TableFactor::Table {
|
||||||
|
name: ObjectName(vec![Ident {
|
||||||
|
value: "test".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
}]),
|
||||||
|
alias: None,
|
||||||
|
args: None,
|
||||||
|
with_hints: vec![],
|
||||||
|
},
|
||||||
|
joins: vec![],
|
||||||
|
}],
|
||||||
|
lateral_views: vec![],
|
||||||
|
selection: Some(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident {
|
||||||
|
value: "d3_date".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
})),
|
||||||
|
op: BinaryOperator::Gt,
|
||||||
|
right: Box::new(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident {
|
||||||
|
value: "d1_date".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
})),
|
||||||
|
op: BinaryOperator::Plus,
|
||||||
|
right: Box::new(Expr::Interval {
|
||||||
|
value: Box::new(Expr::Value(Value::SingleQuotedString(
|
||||||
|
"5 days".to_string(),
|
||||||
|
))),
|
||||||
|
leading_field: None,
|
||||||
|
leading_precision: None,
|
||||||
|
last_field: None,
|
||||||
|
fractional_seconds_precision: None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
op: BinaryOperator::And,
|
||||||
|
right: Box::new(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident {
|
||||||
|
value: "d2_date".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
})),
|
||||||
|
op: BinaryOperator::Gt,
|
||||||
|
right: Box::new(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Identifier(Ident {
|
||||||
|
value: "d1_date".to_string(),
|
||||||
|
quote_style: None,
|
||||||
|
})),
|
||||||
|
op: BinaryOperator::Plus,
|
||||||
|
right: Box::new(Expr::Interval {
|
||||||
|
value: Box::new(Expr::Value(Value::SingleQuotedString(
|
||||||
|
"3 days".to_string(),
|
||||||
|
))),
|
||||||
|
leading_field: None,
|
||||||
|
leading_precision: None,
|
||||||
|
last_field: None,
|
||||||
|
fractional_seconds_precision: None,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
group_by: vec![],
|
||||||
|
cluster_by: vec![],
|
||||||
|
distribute_by: vec![],
|
||||||
|
sort_by: vec![],
|
||||||
|
having: None,
|
||||||
|
qualify: None,
|
||||||
|
}))),
|
||||||
|
order_by: vec![],
|
||||||
|
limit: None,
|
||||||
|
offset: None,
|
||||||
|
fetch: None,
|
||||||
|
lock: None,
|
||||||
|
}))];
|
||||||
|
|
||||||
|
assert_eq!(actual_ast, expected_ast);
|
||||||
|
|
||||||
|
verified_stmt(
|
||||||
|
"SELECT col FROM test \
|
||||||
|
WHERE d3_date > d1_date + INTERVAL '5 days' \
|
||||||
|
AND d2_date > d1_date + INTERVAL '3 days'",
|
||||||
|
);
|
||||||
|
|
||||||
|
verified_stmt(
|
||||||
|
"SELECT col FROM test \
|
||||||
|
WHERE d3_date > d1_date + INTERVAL '5 days' \
|
||||||
|
OR d2_date > d1_date + INTERVAL '3 days'",
|
||||||
|
);
|
||||||
|
|
||||||
|
verified_stmt(
|
||||||
|
"SELECT col FROM test \
|
||||||
|
WHERE d3_date > d1_date + INTERVAL '5 days' \
|
||||||
|
XOR d2_date > d1_date + INTERVAL '3 days'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_at_timezone() {
|
fn parse_at_timezone() {
|
||||||
let zero = Expr::Value(number("0"));
|
let zero = Expr::Value(number("0"));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue