Skip to content

Commit 0f0b327

Browse files
authored
Implement is [not] distinct from (apache#361)
* Implement is [not] distinct from * Simplify message * Clippy
1 parent c9f8a44 commit 0f0b327

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

src/ast/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,14 @@ pub enum Expr {
169169
QualifiedWildcard(Vec<Ident>),
170170
/// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col`
171171
CompoundIdentifier(Vec<Ident>),
172-
/// `IS NULL` expression
172+
/// `IS NULL` operator
173173
IsNull(Box<Expr>),
174-
/// `IS NOT NULL` expression
174+
/// `IS NOT NULL` operator
175175
IsNotNull(Box<Expr>),
176+
/// `IS DISTINCT FROM` operator
177+
IsDistinctFrom(Box<Expr>, Box<Expr>),
178+
/// `IS NOT DISTINCT FROM` operator
179+
IsNotDistinctFrom(Box<Expr>, Box<Expr>),
176180
/// `[ NOT ] IN (val1, val2, ...)`
177181
InList {
178182
expr: Box<Expr>,
@@ -387,6 +391,8 @@ impl fmt::Display for Expr {
387391

388392
write!(f, ")")
389393
}
394+
Expr::IsDistinctFrom(a, b) => write!(f, "{} IS DISTINCT FROM {}", a, b),
395+
Expr::IsNotDistinctFrom(a, b) => write!(f, "{} IS NOT DISTINCT FROM {}", a, b),
390396
Expr::Trim { expr, trim_where } => {
391397
write!(f, "TRIM(")?;
392398
if let Some((ident, trim_char)) = trim_where {

src/dialect/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,8 @@ mod tests {
8787

8888
assert!(dialect_of!(generic_holder is GenericDialect | AnsiDialect),);
8989
assert!(!dialect_of!(generic_holder is AnsiDialect));
90-
91-
assert!(dialect_of!(ansi_holder is AnsiDialect));
92-
assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect),);
93-
assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect),);
90+
assert!(dialect_of!(ansi_holder is AnsiDialect));
91+
assert!(dialect_of!(ansi_holder is GenericDialect | AnsiDialect));
92+
assert!(!dialect_of!(ansi_holder is GenericDialect | MsSqlDialect));
9493
}
9594
}

src/parser.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,8 +917,18 @@ impl<'a> Parser<'a> {
917917
Ok(Expr::IsNull(Box::new(expr)))
918918
} else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) {
919919
Ok(Expr::IsNotNull(Box::new(expr)))
920+
} else if self.parse_keywords(&[Keyword::DISTINCT, Keyword::FROM]) {
921+
let expr2 = self.parse_expr()?;
922+
Ok(Expr::IsDistinctFrom(Box::new(expr), Box::new(expr2)))
923+
} else if self.parse_keywords(&[Keyword::NOT, Keyword::DISTINCT, Keyword::FROM])
924+
{
925+
let expr2 = self.parse_expr()?;
926+
Ok(Expr::IsNotDistinctFrom(Box::new(expr), Box::new(expr2)))
920927
} else {
921-
self.expected("NULL or NOT NULL after IS", self.peek_token())
928+
self.expected(
929+
"[NOT] NULL or [NOT] DISTINCT FROM after IS",
930+
self.peek_token(),
931+
)
922932
}
923933
}
924934
Keyword::NOT | Keyword::IN | Keyword::BETWEEN => {

tests/sqlparser_common.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,31 @@ fn parse_is_not_null() {
586586
);
587587
}
588588

589+
#[test]
590+
fn parse_is_distinct_from() {
591+
use self::Expr::*;
592+
let sql = "a IS DISTINCT FROM b";
593+
assert_eq!(
594+
IsDistinctFrom(
595+
Box::new(Identifier(Ident::new("a"))),
596+
Box::new(Identifier(Ident::new("b")))
597+
),
598+
verified_expr(sql)
599+
);
600+
}
601+
#[test]
602+
fn parse_is_not_distinct_from() {
603+
use self::Expr::*;
604+
let sql = "a IS NOT DISTINCT FROM b";
605+
assert_eq!(
606+
IsNotDistinctFrom(
607+
Box::new(Identifier(Ident::new("a"))),
608+
Box::new(Identifier(Ident::new("b")))
609+
),
610+
verified_expr(sql)
611+
);
612+
}
613+
589614
#[test]
590615
fn parse_not_precedence() {
591616
// NOT has higher precedence than OR/AND, so the following must parse as (NOT true) OR true

0 commit comments

Comments
 (0)