mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-24 07:54:06 +00:00
Add support for the LIKE ANY and ILIKE ANY pattern-matching condition (#1456)
This commit is contained in:
parent
32a126b27c
commit
e849f7f143
3 changed files with 36 additions and 4 deletions
|
@ -613,6 +613,9 @@ pub enum Expr {
|
||||||
/// `[NOT] LIKE <pattern> [ESCAPE <escape_character>]`
|
/// `[NOT] LIKE <pattern> [ESCAPE <escape_character>]`
|
||||||
Like {
|
Like {
|
||||||
negated: bool,
|
negated: bool,
|
||||||
|
// Snowflake supports the ANY keyword to match against a list of patterns
|
||||||
|
// https://docs.snowflake.com/en/sql-reference/functions/like_any
|
||||||
|
any: bool,
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
pattern: Box<Expr>,
|
pattern: Box<Expr>,
|
||||||
escape_char: Option<String>,
|
escape_char: Option<String>,
|
||||||
|
@ -620,6 +623,9 @@ pub enum Expr {
|
||||||
/// `ILIKE` (case-insensitive `LIKE`)
|
/// `ILIKE` (case-insensitive `LIKE`)
|
||||||
ILike {
|
ILike {
|
||||||
negated: bool,
|
negated: bool,
|
||||||
|
// Snowflake supports the ANY keyword to match against a list of patterns
|
||||||
|
// https://docs.snowflake.com/en/sql-reference/functions/like_any
|
||||||
|
any: bool,
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
pattern: Box<Expr>,
|
pattern: Box<Expr>,
|
||||||
escape_char: Option<String>,
|
escape_char: Option<String>,
|
||||||
|
@ -1242,20 +1248,23 @@ impl fmt::Display for Expr {
|
||||||
expr,
|
expr,
|
||||||
pattern,
|
pattern,
|
||||||
escape_char,
|
escape_char,
|
||||||
|
any,
|
||||||
} => match escape_char {
|
} => match escape_char {
|
||||||
Some(ch) => write!(
|
Some(ch) => write!(
|
||||||
f,
|
f,
|
||||||
"{} {}LIKE {} ESCAPE '{}'",
|
"{} {}LIKE {}{} ESCAPE '{}'",
|
||||||
expr,
|
expr,
|
||||||
if *negated { "NOT " } else { "" },
|
if *negated { "NOT " } else { "" },
|
||||||
|
if *any { "ANY " } else { "" },
|
||||||
pattern,
|
pattern,
|
||||||
ch
|
ch
|
||||||
),
|
),
|
||||||
_ => write!(
|
_ => write!(
|
||||||
f,
|
f,
|
||||||
"{} {}LIKE {}",
|
"{} {}LIKE {}{}",
|
||||||
expr,
|
expr,
|
||||||
if *negated { "NOT " } else { "" },
|
if *negated { "NOT " } else { "" },
|
||||||
|
if *any { "ANY " } else { "" },
|
||||||
pattern
|
pattern
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -1264,20 +1273,23 @@ impl fmt::Display for Expr {
|
||||||
expr,
|
expr,
|
||||||
pattern,
|
pattern,
|
||||||
escape_char,
|
escape_char,
|
||||||
|
any,
|
||||||
} => match escape_char {
|
} => match escape_char {
|
||||||
Some(ch) => write!(
|
Some(ch) => write!(
|
||||||
f,
|
f,
|
||||||
"{} {}ILIKE {} ESCAPE '{}'",
|
"{} {}ILIKE {}{} ESCAPE '{}'",
|
||||||
expr,
|
expr,
|
||||||
if *negated { "NOT " } else { "" },
|
if *negated { "NOT " } else { "" },
|
||||||
|
if *any { "ANY" } else { "" },
|
||||||
pattern,
|
pattern,
|
||||||
ch
|
ch
|
||||||
),
|
),
|
||||||
_ => write!(
|
_ => write!(
|
||||||
f,
|
f,
|
||||||
"{} {}ILIKE {}",
|
"{} {}ILIKE {}{}",
|
||||||
expr,
|
expr,
|
||||||
if *negated { "NOT " } else { "" },
|
if *negated { "NOT " } else { "" },
|
||||||
|
if *any { "ANY " } else { "" },
|
||||||
pattern
|
pattern
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
@ -2749,6 +2749,7 @@ impl<'a> Parser<'a> {
|
||||||
} else if self.parse_keyword(Keyword::LIKE) {
|
} else if self.parse_keyword(Keyword::LIKE) {
|
||||||
Ok(Expr::Like {
|
Ok(Expr::Like {
|
||||||
negated,
|
negated,
|
||||||
|
any: self.parse_keyword(Keyword::ANY),
|
||||||
expr: Box::new(expr),
|
expr: Box::new(expr),
|
||||||
pattern: Box::new(
|
pattern: Box::new(
|
||||||
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
|
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
|
||||||
|
@ -2758,6 +2759,7 @@ impl<'a> Parser<'a> {
|
||||||
} else if self.parse_keyword(Keyword::ILIKE) {
|
} else if self.parse_keyword(Keyword::ILIKE) {
|
||||||
Ok(Expr::ILike {
|
Ok(Expr::ILike {
|
||||||
negated,
|
negated,
|
||||||
|
any: self.parse_keyword(Keyword::ANY),
|
||||||
expr: Box::new(expr),
|
expr: Box::new(expr),
|
||||||
pattern: Box::new(
|
pattern: Box::new(
|
||||||
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
|
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
|
||||||
|
|
|
@ -1550,6 +1550,7 @@ fn parse_not_precedence() {
|
||||||
negated: true,
|
negated: true,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1580,6 +1581,7 @@ fn parse_null_like() {
|
||||||
SelectItem::ExprWithAlias {
|
SelectItem::ExprWithAlias {
|
||||||
expr: Expr::Like {
|
expr: Expr::Like {
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("column1"))),
|
expr: Box::new(Expr::Identifier(Ident::new("column1"))),
|
||||||
|
any: false,
|
||||||
negated: false,
|
negated: false,
|
||||||
pattern: Box::new(Expr::Value(Value::Null)),
|
pattern: Box::new(Expr::Value(Value::Null)),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
@ -1595,6 +1597,7 @@ fn parse_null_like() {
|
||||||
SelectItem::ExprWithAlias {
|
SelectItem::ExprWithAlias {
|
||||||
expr: Expr::Like {
|
expr: Expr::Like {
|
||||||
expr: Box::new(Expr::Value(Value::Null)),
|
expr: Box::new(Expr::Value(Value::Null)),
|
||||||
|
any: false,
|
||||||
negated: false,
|
negated: false,
|
||||||
pattern: Box::new(Expr::Identifier(Ident::new("column1"))),
|
pattern: Box::new(Expr::Identifier(Ident::new("column1"))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
@ -1622,6 +1625,7 @@ fn parse_ilike() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
},
|
},
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -1638,6 +1642,7 @@ fn parse_ilike() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: Some('^'.to_string()),
|
escape_char: Some('^'.to_string()),
|
||||||
|
any: false,
|
||||||
},
|
},
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -1655,6 +1660,7 @@ fn parse_ilike() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
})),
|
})),
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -1677,6 +1683,7 @@ fn parse_like() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
},
|
},
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -1693,6 +1700,7 @@ fn parse_like() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: Some('^'.to_string()),
|
escape_char: Some('^'.to_string()),
|
||||||
|
any: false,
|
||||||
},
|
},
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -1710,6 +1718,7 @@ fn parse_like() {
|
||||||
negated,
|
negated,
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
})),
|
})),
|
||||||
select.selection.unwrap()
|
select.selection.unwrap()
|
||||||
);
|
);
|
||||||
|
@ -10130,6 +10139,7 @@ fn test_selective_aggregation() {
|
||||||
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
expr: Box::new(Expr::Identifier(Ident::new("name"))),
|
||||||
pattern: Box::new(Expr::Value(Value::SingleQuotedString("a%".to_owned()))),
|
pattern: Box::new(Expr::Value(Value::SingleQuotedString("a%".to_owned()))),
|
||||||
escape_char: None,
|
escape_char: None,
|
||||||
|
any: false,
|
||||||
})),
|
})),
|
||||||
null_treatment: None,
|
null_treatment: None,
|
||||||
over: None,
|
over: None,
|
||||||
|
@ -11291,3 +11301,11 @@ fn test_alter_policy() {
|
||||||
"sql parser error: Expected: (, found: EOF"
|
"sql parser error: Expected: (, found: EOF"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_select_where_with_like_or_ilike_any() {
|
||||||
|
verified_stmt(r#"SELECT * FROM x WHERE a ILIKE ANY '%abc%'"#);
|
||||||
|
verified_stmt(r#"SELECT * FROM x WHERE a LIKE ANY '%abc%'"#);
|
||||||
|
verified_stmt(r#"SELECT * FROM x WHERE a ILIKE ANY ('%Jo%oe%', 'T%e')"#);
|
||||||
|
verified_stmt(r#"SELECT * FROM x WHERE a LIKE ANY ('%Jo%oe%', 'T%e')"#);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue