Skip to content

Commit 57083a0

Browse files
authored
Fix interval parsing logic and precedence (#705)
* initial fix * add comma * add test * style * add more tests * codestyle fix
1 parent 4b1dc1a commit 57083a0

File tree

2 files changed

+146
-1
lines changed

2 files changed

+146
-1
lines changed

src/parser.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,36 @@ impl<'a> Parser<'a> {
363363
Ok(expr)
364364
}
365365

366+
pub fn parse_interval_expr(&mut self) -> Result<Expr, ParserError> {
367+
let precedence = 0;
368+
let mut expr = self.parse_prefix()?;
369+
370+
loop {
371+
let next_precedence = self.get_next_interval_precedence()?;
372+
373+
if precedence >= next_precedence {
374+
break;
375+
}
376+
377+
expr = self.parse_infix(expr, next_precedence)?;
378+
}
379+
380+
Ok(expr)
381+
}
382+
383+
/// Get the precedence of the next token
384+
/// With AND, OR, and XOR
385+
pub fn get_next_interval_precedence(&self) -> Result<u8, ParserError> {
386+
let token = self.peek_token();
387+
388+
match token {
389+
Token::Word(w) if w.keyword == Keyword::AND => Ok(0),
390+
Token::Word(w) if w.keyword == Keyword::OR => Ok(0),
391+
Token::Word(w) if w.keyword == Keyword::XOR => Ok(0),
392+
_ => self.get_next_precedence(),
393+
}
394+
}
395+
366396
pub fn parse_assert(&mut self) -> Result<Statement, ParserError> {
367397
let condition = self.parse_expr()?;
368398
let message = if self.parse_keyword(Keyword::AS) {
@@ -1200,7 +1230,7 @@ impl<'a> Parser<'a> {
12001230

12011231
// The first token in an interval is a string literal which specifies
12021232
// the duration of the interval.
1203-
let value = self.parse_expr()?;
1233+
let value = self.parse_interval_expr()?;
12041234

12051235
// Following the string literal is a qualifier which indicates the units
12061236
// of the duration specified in the string literal.

tests/sqlparser_common.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,6 +3237,121 @@ fn parse_interval() {
32373237
);
32383238
}
32393239

3240+
#[test]
3241+
fn parse_interval_and_or_xor() {
3242+
let sql = "SELECT col FROM test \
3243+
WHERE d3_date > d1_date + INTERVAL '5 days' \
3244+
AND d2_date > d1_date + INTERVAL '3 days'";
3245+
3246+
let actual_ast = Parser::parse_sql(&GenericDialect {}, sql).unwrap();
3247+
3248+
let expected_ast = vec![Statement::Query(Box::new(Query {
3249+
with: None,
3250+
body: Box::new(SetExpr::Select(Box::new(Select {
3251+
distinct: false,
3252+
top: None,
3253+
projection: vec![UnnamedExpr(Expr::Identifier(Ident {
3254+
value: "col".to_string(),
3255+
quote_style: None,
3256+
}))],
3257+
into: None,
3258+
from: vec![TableWithJoins {
3259+
relation: TableFactor::Table {
3260+
name: ObjectName(vec![Ident {
3261+
value: "test".to_string(),
3262+
quote_style: None,
3263+
}]),
3264+
alias: None,
3265+
args: None,
3266+
with_hints: vec![],
3267+
},
3268+
joins: vec![],
3269+
}],
3270+
lateral_views: vec![],
3271+
selection: Some(Expr::BinaryOp {
3272+
left: Box::new(Expr::BinaryOp {
3273+
left: Box::new(Expr::Identifier(Ident {
3274+
value: "d3_date".to_string(),
3275+
quote_style: None,
3276+
})),
3277+
op: BinaryOperator::Gt,
3278+
right: Box::new(Expr::BinaryOp {
3279+
left: Box::new(Expr::Identifier(Ident {
3280+
value: "d1_date".to_string(),
3281+
quote_style: None,
3282+
})),
3283+
op: BinaryOperator::Plus,
3284+
right: Box::new(Expr::Interval {
3285+
value: Box::new(Expr::Value(Value::SingleQuotedString(
3286+
"5 days".to_string(),
3287+
))),
3288+
leading_field: None,
3289+
leading_precision: None,
3290+
last_field: None,
3291+
fractional_seconds_precision: None,
3292+
}),
3293+
}),
3294+
}),
3295+
op: BinaryOperator::And,
3296+
right: Box::new(Expr::BinaryOp {
3297+
left: Box::new(Expr::Identifier(Ident {
3298+
value: "d2_date".to_string(),
3299+
quote_style: None,
3300+
})),
3301+
op: BinaryOperator::Gt,
3302+
right: Box::new(Expr::BinaryOp {
3303+
left: Box::new(Expr::Identifier(Ident {
3304+
value: "d1_date".to_string(),
3305+
quote_style: None,
3306+
})),
3307+
op: BinaryOperator::Plus,
3308+
right: Box::new(Expr::Interval {
3309+
value: Box::new(Expr::Value(Value::SingleQuotedString(
3310+
"3 days".to_string(),
3311+
))),
3312+
leading_field: None,
3313+
leading_precision: None,
3314+
last_field: None,
3315+
fractional_seconds_precision: None,
3316+
}),
3317+
}),
3318+
}),
3319+
}),
3320+
group_by: vec![],
3321+
cluster_by: vec![],
3322+
distribute_by: vec![],
3323+
sort_by: vec![],
3324+
having: None,
3325+
qualify: None,
3326+
}))),
3327+
order_by: vec![],
3328+
limit: None,
3329+
offset: None,
3330+
fetch: None,
3331+
lock: None,
3332+
}))];
3333+
3334+
assert_eq!(actual_ast, expected_ast);
3335+
3336+
verified_stmt(
3337+
"SELECT col FROM test \
3338+
WHERE d3_date > d1_date + INTERVAL '5 days' \
3339+
AND d2_date > d1_date + INTERVAL '3 days'",
3340+
);
3341+
3342+
verified_stmt(
3343+
"SELECT col FROM test \
3344+
WHERE d3_date > d1_date + INTERVAL '5 days' \
3345+
OR d2_date > d1_date + INTERVAL '3 days'",
3346+
);
3347+
3348+
verified_stmt(
3349+
"SELECT col FROM test \
3350+
WHERE d3_date > d1_date + INTERVAL '5 days' \
3351+
XOR d2_date > d1_date + INTERVAL '3 days'",
3352+
);
3353+
}
3354+
32403355
#[test]
32413356
fn parse_at_timezone() {
32423357
let zero = Expr::Value(number("0"));

0 commit comments

Comments
 (0)