Skip to content

Commit c3c77dd

Browse files
maxcountrymanilya-biryukov
authored andcommitted
provide ILIKE support
cherry-picked from a9e6f77. This introduces support for ILIKE and NOT ILIKE. ILIKE is the case-insensitive variant of LIKE. Systems such as Postgres, Redshift, and Snowflake provide this variant.[1][2][3] [1] https://www.postgresql.org/docs/7.3/functions-matching.html [2] https://docs.aws.amazon.com/redshift/latest/dg/r_patternmatching_condition_like.html [3] https://docs.snowflake.com/en/sql-reference/functions/ilike.html
1 parent b1d144a commit c3c77dd

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

src/ast/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,7 @@ impl fmt::Display for TransactionIsolationLevel {
15851585
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15861586
pub enum ShowStatementFilter {
15871587
Like(String),
1588+
ILike(String),
15881589
Where(Expr),
15891590
}
15901591

@@ -1593,6 +1594,7 @@ impl fmt::Display for ShowStatementFilter {
15931594
use ShowStatementFilter::*;
15941595
match self {
15951596
Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
1597+
ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)),
15961598
Where(expr) => write!(f, "WHERE {}", expr),
15971599
}
15981600
}

src/ast/operator.rs

+4
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ pub enum BinaryOperator {
7272
Or,
7373
Like,
7474
NotLike,
75+
ILike,
76+
NotILike,
7577
BitwiseOr,
7678
BitwiseAnd,
7779
BitwiseXor,
@@ -100,6 +102,8 @@ impl fmt::Display for BinaryOperator {
100102
BinaryOperator::Or => "OR",
101103
BinaryOperator::Like => "LIKE",
102104
BinaryOperator::NotLike => "NOT LIKE",
105+
BinaryOperator::ILike => "ILIKE",
106+
BinaryOperator::NotILike => "NOT ILIKE",
103107
BinaryOperator::BitwiseOr => "|",
104108
BinaryOperator::BitwiseAnd => "&",
105109
BinaryOperator::BitwiseXor => "^",

src/dialect/keywords.rs

+1
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ define_keywords!(
237237
IDENTITY,
238238
IF,
239239
IGNORE,
240+
ILIKE,
240241
IN,
241242
INDEX,
242243
INDICATOR,

src/parser.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -875,9 +875,12 @@ impl<'a> Parser<'a> {
875875
Keyword::AND => Some(BinaryOperator::And),
876876
Keyword::OR => Some(BinaryOperator::Or),
877877
Keyword::LIKE => Some(BinaryOperator::Like),
878+
Keyword::ILIKE => Some(BinaryOperator::ILike),
878879
Keyword::NOT => {
879880
if self.parse_keyword(Keyword::LIKE) {
880881
Some(BinaryOperator::NotLike)
882+
} else if self.parse_keyword(Keyword::ILIKE) {
883+
Some(BinaryOperator::NotILike)
881884
} else {
882885
None
883886
}
@@ -1011,12 +1014,14 @@ impl<'a> Parser<'a> {
10111014
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
10121015
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
10131016
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
1017+
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
10141018
_ => Ok(0),
10151019
},
10161020
Token::Word(w) if w.keyword == Keyword::IS => Ok(17),
10171021
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
10181022
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
10191023
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
1024+
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
10201025
Token::Eq
10211026
| Token::Lt
10221027
| Token::LtEq
@@ -1517,7 +1522,7 @@ impl<'a> Parser<'a> {
15171522
) -> Result<Statement, ParserError> {
15181523
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
15191524
let table_name = self.parse_object_name()?;
1520-
let like = if self.parse_keyword(Keyword::LIKE) {
1525+
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
15211526
self.parse_object_name().ok()
15221527
} else {
15231528
None
@@ -2592,6 +2597,10 @@ impl<'a> Parser<'a> {
25922597
Ok(Some(ShowStatementFilter::Like(
25932598
self.parse_literal_string()?,
25942599
)))
2600+
} else if self.parse_keyword(Keyword::ILIKE) {
2601+
Ok(Some(ShowStatementFilter::ILike(
2602+
self.parse_literal_string()?,
2603+
)))
25952604
} else if self.parse_keyword(Keyword::WHERE) {
25962605
Ok(Some(ShowStatementFilter::Where(self.parse_expr()?)))
25972606
} else {

tests/sqlparser_common.rs

+45
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,51 @@ fn parse_like() {
687687
chk(true);
688688
}
689689

690+
#[test]
691+
fn parse_ilike() {
692+
fn chk(negated: bool) {
693+
let sql = &format!(
694+
"SELECT * FROM customers WHERE name {}ILIKE '%a'",
695+
if negated { "NOT " } else { "" }
696+
);
697+
let select = verified_only_select(sql);
698+
assert_eq!(
699+
Expr::BinaryOp {
700+
left: Box::new(Expr::Identifier(Ident::new("name"))),
701+
op: if negated {
702+
BinaryOperator::NotILike
703+
} else {
704+
BinaryOperator::ILike
705+
},
706+
right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
707+
},
708+
select.selection.unwrap()
709+
);
710+
711+
// This statement tests that LIKE and NOT LIKE have the same precedence.
712+
// This was previously mishandled (#81).
713+
let sql = &format!(
714+
"SELECT * FROM customers WHERE name {}ILIKE '%a' IS NULL",
715+
if negated { "NOT " } else { "" }
716+
);
717+
let select = verified_only_select(sql);
718+
assert_eq!(
719+
Expr::IsNull(Box::new(Expr::BinaryOp {
720+
left: Box::new(Expr::Identifier(Ident::new("name"))),
721+
op: if negated {
722+
BinaryOperator::NotILike
723+
} else {
724+
BinaryOperator::ILike
725+
},
726+
right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
727+
})),
728+
select.selection.unwrap()
729+
);
730+
}
731+
chk(false);
732+
chk(true);
733+
}
734+
690735
#[test]
691736
fn parse_in_list() {
692737
fn chk(negated: bool) {

0 commit comments

Comments
 (0)