Add support for the LIKE ANY and ILIKE ANY pattern-matching condition (#1456)

This commit is contained in:
Yoav Cohen 2024-10-04 22:03:38 +02:00 committed by GitHub
parent 32a126b27c
commit e849f7f143
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 36 additions and 4 deletions

View file

@ -613,6 +613,9 @@ pub enum Expr {
/// `[NOT] LIKE <pattern> [ESCAPE <escape_character>]`
Like {
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>,
pattern: Box<Expr>,
escape_char: Option<String>,
@ -620,6 +623,9 @@ pub enum Expr {
/// `ILIKE` (case-insensitive `LIKE`)
ILike {
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>,
pattern: Box<Expr>,
escape_char: Option<String>,
@ -1242,20 +1248,23 @@ impl fmt::Display for Expr {
expr,
pattern,
escape_char,
any,
} => match escape_char {
Some(ch) => write!(
f,
"{} {}LIKE {} ESCAPE '{}'",
"{} {}LIKE {}{} ESCAPE '{}'",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern,
ch
),
_ => write!(
f,
"{} {}LIKE {}",
"{} {}LIKE {}{}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern
),
},
@ -1264,20 +1273,23 @@ impl fmt::Display for Expr {
expr,
pattern,
escape_char,
any,
} => match escape_char {
Some(ch) => write!(
f,
"{} {}ILIKE {} ESCAPE '{}'",
"{} {}ILIKE {}{} ESCAPE '{}'",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY" } else { "" },
pattern,
ch
),
_ => write!(
f,
"{} {}ILIKE {}",
"{} {}ILIKE {}{}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
pattern
),
},

View file

@ -2749,6 +2749,7 @@ impl<'a> Parser<'a> {
} else if self.parse_keyword(Keyword::LIKE) {
Ok(Expr::Like {
negated,
any: self.parse_keyword(Keyword::ANY),
expr: Box::new(expr),
pattern: Box::new(
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,
@ -2758,6 +2759,7 @@ impl<'a> Parser<'a> {
} else if self.parse_keyword(Keyword::ILIKE) {
Ok(Expr::ILike {
negated,
any: self.parse_keyword(Keyword::ANY),
expr: Box::new(expr),
pattern: Box::new(
self.parse_subexpr(self.dialect.prec_value(Precedence::Like))?,

View file

@ -1550,6 +1550,7 @@ fn parse_not_precedence() {
negated: true,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))),
escape_char: None,
any: false,
}),
},
);
@ -1580,6 +1581,7 @@ fn parse_null_like() {
SelectItem::ExprWithAlias {
expr: Expr::Like {
expr: Box::new(Expr::Identifier(Ident::new("column1"))),
any: false,
negated: false,
pattern: Box::new(Expr::Value(Value::Null)),
escape_char: None,
@ -1595,6 +1597,7 @@ fn parse_null_like() {
SelectItem::ExprWithAlias {
expr: Expr::Like {
expr: Box::new(Expr::Value(Value::Null)),
any: false,
negated: false,
pattern: Box::new(Expr::Identifier(Ident::new("column1"))),
escape_char: None,
@ -1622,6 +1625,7 @@ fn parse_ilike() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: None,
any: false,
},
select.selection.unwrap()
);
@ -1638,6 +1642,7 @@ fn parse_ilike() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: Some('^'.to_string()),
any: false,
},
select.selection.unwrap()
);
@ -1655,6 +1660,7 @@ fn parse_ilike() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: None,
any: false,
})),
select.selection.unwrap()
);
@ -1677,6 +1683,7 @@ fn parse_like() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: None,
any: false,
},
select.selection.unwrap()
);
@ -1693,6 +1700,7 @@ fn parse_like() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: Some('^'.to_string()),
any: false,
},
select.selection.unwrap()
);
@ -1710,6 +1718,7 @@ fn parse_like() {
negated,
pattern: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
escape_char: None,
any: false,
})),
select.selection.unwrap()
);
@ -10130,6 +10139,7 @@ fn test_selective_aggregation() {
expr: Box::new(Expr::Identifier(Ident::new("name"))),
pattern: Box::new(Expr::Value(Value::SingleQuotedString("a%".to_owned()))),
escape_char: None,
any: false,
})),
null_treatment: None,
over: None,
@ -11291,3 +11301,11 @@ fn test_alter_policy() {
"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')"#);
}