Skip to content

Commit bc53bdd

Browse files
committed
provide ILIKE support
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 43fef23 commit bc53bdd

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

src/ast/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,7 @@ impl fmt::Display for TransactionIsolationLevel {
15391539
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
15401540
pub enum ShowStatementFilter {
15411541
Like(String),
1542+
ILike(String),
15421543
Where(Expr),
15431544
}
15441545

@@ -1547,6 +1548,7 @@ impl fmt::Display for ShowStatementFilter {
15471548
use ShowStatementFilter::*;
15481549
match self {
15491550
Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
1551+
ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)),
15501552
Where(expr) => write!(f, "WHERE {}", expr),
15511553
}
15521554
}

src/ast/operator.rs

Lines changed: 4 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ define_keywords!(
236236
IDENTITY,
237237
IF,
238238
IGNORE,
239+
ILIKE,
239240
IN,
240241
INDEX,
241242
INDICATOR,

src/parser.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -825,9 +825,12 @@ impl<'a> Parser<'a> {
825825
Keyword::AND => Some(BinaryOperator::And),
826826
Keyword::OR => Some(BinaryOperator::Or),
827827
Keyword::LIKE => Some(BinaryOperator::Like),
828+
Keyword::ILIKE => Some(BinaryOperator::ILike),
828829
Keyword::NOT => {
829830
if self.parse_keyword(Keyword::LIKE) {
830831
Some(BinaryOperator::NotLike)
832+
} else if self.parse_keyword(Keyword::ILIKE) {
833+
Some(BinaryOperator::NotILike)
831834
} else {
832835
None
833836
}
@@ -961,12 +964,14 @@ impl<'a> Parser<'a> {
961964
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
962965
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
963966
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
967+
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
964968
_ => Ok(0),
965969
},
966970
Token::Word(w) if w.keyword == Keyword::IS => Ok(17),
967971
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
968972
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
969973
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
974+
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
970975
Token::Eq
971976
| Token::Lt
972977
| Token::LtEq
@@ -1458,7 +1463,7 @@ impl<'a> Parser<'a> {
14581463
) -> Result<Statement, ParserError> {
14591464
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
14601465
let table_name = self.parse_object_name()?;
1461-
let like = if self.parse_keyword(Keyword::LIKE) {
1466+
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
14621467
self.parse_object_name().ok()
14631468
} else {
14641469
None
@@ -2484,6 +2489,10 @@ impl<'a> Parser<'a> {
24842489
Ok(Some(ShowStatementFilter::Like(
24852490
self.parse_literal_string()?,
24862491
)))
2492+
} else if self.parse_keyword(Keyword::ILIKE) {
2493+
Ok(Some(ShowStatementFilter::ILike(
2494+
self.parse_literal_string()?,
2495+
)))
24872496
} else if self.parse_keyword(Keyword::WHERE) {
24882497
Ok(Some(ShowStatementFilter::Where(self.parse_expr()?)))
24892498
} else {

tests/sqlparser_common.rs

Lines changed: 45 additions & 0 deletions
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)