Skip to content

Commit

Permalink
Merge pull request #300 from maxcountryman/feature/ilike
Browse files Browse the repository at this point in the history
provide ILIKE support
  • Loading branch information
maxcountryman committed Mar 22, 2021
2 parents 1e87ab8 + a9e6f77 commit 35ef0ee
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/ast/mod.rs
Expand Up @@ -1546,6 +1546,7 @@ impl fmt::Display for TransactionIsolationLevel {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ShowStatementFilter {
Like(String),
ILike(String),
Where(Expr),
}

Expand All @@ -1554,6 +1555,7 @@ impl fmt::Display for ShowStatementFilter {
use ShowStatementFilter::*;
match self {
Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)),
ILike(pattern) => write!(f, "ILIKE {}", value::escape_single_quote_string(pattern)),
Where(expr) => write!(f, "WHERE {}", expr),
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/ast/operator.rs
Expand Up @@ -72,6 +72,8 @@ pub enum BinaryOperator {
Or,
Like,
NotLike,
ILike,
NotILike,
BitwiseOr,
BitwiseAnd,
BitwiseXor,
Expand Down Expand Up @@ -100,6 +102,8 @@ impl fmt::Display for BinaryOperator {
BinaryOperator::Or => "OR",
BinaryOperator::Like => "LIKE",
BinaryOperator::NotLike => "NOT LIKE",
BinaryOperator::ILike => "ILIKE",
BinaryOperator::NotILike => "NOT ILIKE",
BinaryOperator::BitwiseOr => "|",
BinaryOperator::BitwiseAnd => "&",
BinaryOperator::BitwiseXor => "^",
Expand Down
1 change: 1 addition & 0 deletions src/dialect/keywords.rs
Expand Up @@ -236,6 +236,7 @@ define_keywords!(
IDENTITY,
IF,
IGNORE,
ILIKE,
IN,
INDEX,
INDICATOR,
Expand Down
11 changes: 10 additions & 1 deletion src/parser.rs
Expand Up @@ -839,9 +839,12 @@ impl<'a> Parser<'a> {
Keyword::AND => Some(BinaryOperator::And),
Keyword::OR => Some(BinaryOperator::Or),
Keyword::LIKE => Some(BinaryOperator::Like),
Keyword::ILIKE => Some(BinaryOperator::ILike),
Keyword::NOT => {
if self.parse_keyword(Keyword::LIKE) {
Some(BinaryOperator::NotLike)
} else if self.parse_keyword(Keyword::ILIKE) {
Some(BinaryOperator::NotILike)
} else {
None
}
Expand Down Expand Up @@ -975,12 +978,14 @@ impl<'a> Parser<'a> {
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
_ => Ok(0),
},
Token::Word(w) if w.keyword == Keyword::IS => Ok(17),
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::BETWEEN_PREC),
Token::Eq
| Token::Lt
| Token::LtEq
Expand Down Expand Up @@ -1472,7 +1477,7 @@ impl<'a> Parser<'a> {
) -> Result<Statement, ParserError> {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let table_name = self.parse_object_name()?;
let like = if self.parse_keyword(Keyword::LIKE) {
let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) {
self.parse_object_name().ok()
} else {
None
Expand Down Expand Up @@ -2497,6 +2502,10 @@ impl<'a> Parser<'a> {
Ok(Some(ShowStatementFilter::Like(
self.parse_literal_string()?,
)))
} else if self.parse_keyword(Keyword::ILIKE) {
Ok(Some(ShowStatementFilter::ILike(
self.parse_literal_string()?,
)))
} else if self.parse_keyword(Keyword::WHERE) {
Ok(Some(ShowStatementFilter::Where(self.parse_expr()?)))
} else {
Expand Down
45 changes: 45 additions & 0 deletions tests/sqlparser_common.rs
Expand Up @@ -687,6 +687,51 @@ fn parse_like() {
chk(true);
}

#[test]
fn parse_ilike() {
fn chk(negated: bool) {
let sql = &format!(
"SELECT * FROM customers WHERE name {}ILIKE '%a'",
if negated { "NOT " } else { "" }
);
let select = verified_only_select(sql);
assert_eq!(
Expr::BinaryOp {
left: Box::new(Expr::Identifier(Ident::new("name"))),
op: if negated {
BinaryOperator::NotILike
} else {
BinaryOperator::ILike
},
right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
},
select.selection.unwrap()
);

// This statement tests that LIKE and NOT LIKE have the same precedence.
// This was previously mishandled (#81).
let sql = &format!(
"SELECT * FROM customers WHERE name {}ILIKE '%a' IS NULL",
if negated { "NOT " } else { "" }
);
let select = verified_only_select(sql);
assert_eq!(
Expr::IsNull(Box::new(Expr::BinaryOp {
left: Box::new(Expr::Identifier(Ident::new("name"))),
op: if negated {
BinaryOperator::NotILike
} else {
BinaryOperator::ILike
},
right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))),
})),
select.selection.unwrap()
);
}
chk(false);
chk(true);
}

#[test]
fn parse_in_list() {
fn chk(negated: bool) {
Expand Down

0 comments on commit 35ef0ee

Please sign in to comment.