Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4071,11 +4071,11 @@ impl<'a> Parser<'a> {
} else if self.parse_keywords(&[Keyword::NOT, Keyword::UNKNOWN]) {
Ok(Expr::IsNotUnknown(Box::new(expr)))
} else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::FROM]) {
let expr2 = self.parse_expr()?;
let expr2 = self.parse_subexpr(precedence)?;
Ok(Expr::IsDistinctFrom(Box::new(expr), Box::new(expr2)))
} else if self.parse_keywords(&[Keyword::NOT, Keyword::DISTINCT, Keyword::FROM])
{
let expr2 = self.parse_expr()?;
let expr2 = self.parse_subexpr(precedence)?;
Ok(Expr::IsNotDistinctFrom(Box::new(expr), Box::new(expr2)))
} else if self.parse_keyword(Keyword::JSON) {
self.parse_is_json_predicate(expr, false)
Expand Down
103 changes: 103 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1984,6 +1984,109 @@ fn parse_is_not_distinct_from() {
);
}

#[test]
fn parse_is_distinct_from_precedence() {
let ident = |name: &str| Box::new(Expr::Identifier(Ident::new(name)));

// IS DISTINCT FROM has higher precedence than AND, so the following parses as
// (a IS DISTINCT FROM b) AND c
let sql = "a IS DISTINCT FROM b AND c";
assert_eq!(
verified_expr(sql),
Expr::BinaryOp {
left: Box::new(Expr::IsDistinctFrom(ident("a"), ident("b"))),
op: BinaryOperator::And,
right: ident("c"),
},
);

// The negated form binds the same way, parsing as (a IS NOT DISTINCT FROM b) AND c
let sql = "a IS NOT DISTINCT FROM b AND c";
assert_eq!(
verified_expr(sql),
Expr::BinaryOp {
left: Box::new(Expr::IsNotDistinctFrom(ident("a"), ident("b"))),
op: BinaryOperator::And,
right: ident("c"),
},
);

// OR is lower still, so the following parses as (a IS DISTINCT FROM b) OR c
let sql = "a IS DISTINCT FROM b OR c";
assert_eq!(
verified_expr(sql),
Expr::BinaryOp {
left: Box::new(Expr::IsDistinctFrom(ident("a"), ident("b"))),
op: BinaryOperator::Or,
right: ident("c"),
},
);

// Arithmetic binds tighter, so it joins the right operand: a IS DISTINCT FROM (b + c)
let sql = "a IS DISTINCT FROM b + c";
assert_eq!(
verified_expr(sql),
Expr::IsDistinctFrom(
ident("a"),
Box::new(Expr::BinaryOp {
left: ident("b"),
op: BinaryOperator::Plus,
right: ident("c"),
}),
),
);

// So do the comparison operators, giving a IS DISTINCT FROM (b = c)
let sql = "a IS DISTINCT FROM b = c";
assert_eq!(
verified_expr(sql),
Expr::IsDistinctFrom(
ident("a"),
Box::new(Expr::BinaryOp {
left: ident("b"),
op: BinaryOperator::Eq,
right: ident("c"),
}),
),
);

// NOT is lower, so the following parses as NOT (a IS DISTINCT FROM b)
let sql = "NOT a IS DISTINCT FROM b";
assert_eq!(
verified_expr(sql),
Expr::UnaryOp {
op: UnaryOperator::Not,
expr: Box::new(Expr::IsDistinctFrom(ident("a"), ident("b"))),
},
);

// Parentheses still put a conjunction in the right operand
let sql = "a IS DISTINCT FROM (b AND c)";
assert_eq!(
verified_expr(sql),
Expr::IsDistinctFrom(
ident("a"),
Box::new(Expr::Nested(Box::new(Expr::BinaryOp {
left: ident("b"),
op: BinaryOperator::And,
right: ident("c"),
}))),
),
);

// Both operands of an AND can be a comparison of their own
let sql = "SELECT * FROM t WHERE a IS DISTINCT FROM b AND c IS NOT DISTINCT FROM d";
let select = verified_only_select(sql);
assert_eq!(
select.selection,
Some(Expr::BinaryOp {
left: Box::new(Expr::IsDistinctFrom(ident("a"), ident("b"))),
op: BinaryOperator::And,
right: Box::new(Expr::IsNotDistinctFrom(ident("c"), ident("d"))),
}),
);
}

#[test]
fn parse_not_precedence() {
// NOT has higher precedence than OR/AND, so the following must parse as (NOT true) OR true
Expand Down
Loading