Skip to content

Commit

Permalink
Support mysql RLIKE and REGEXP binary operators (#1017)
Browse files Browse the repository at this point in the history
  • Loading branch information
lovasoa authored Oct 20, 2023
1 parent 88510f6 commit c03586b
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 3 deletions.
21 changes: 21 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,14 @@ pub enum Expr {
pattern: Box<Expr>,
escape_char: Option<char>,
},
/// MySQL: RLIKE regex or REGEXP regex
RLike {
negated: bool,
expr: Box<Expr>,
pattern: Box<Expr>,
// true for REGEXP, false for RLIKE (no difference in semantics)
regexp: bool,
},
/// Any operation e.g. `foo > ANY(bar)`, comparison operator is one of [=, >, <, =>, =<, !=]
AnyOp {
left: Box<Expr>,
Expand Down Expand Up @@ -740,6 +748,19 @@ impl fmt::Display for Expr {
pattern
),
},
Expr::RLike {
negated,
expr,
pattern,
regexp,
} => write!(
f,
"{} {}{} {}",
expr,
if *negated { "NOT " } else { "" },
if *regexp { "REGEXP" } else { "RLIKE" },
pattern
),
Expr::SimilarTo {
negated,
expr,
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ define_keywords!(
REFERENCES,
REFERENCING,
REGCLASS,
REGEXP,
REGR_AVGX,
REGR_AVGY,
REGR_COUNT,
Expand All @@ -524,6 +525,7 @@ define_keywords!(
RETURNS,
REVOKE,
RIGHT,
RLIKE,
ROLE,
ROLLBACK,
ROLLUP,
Expand Down
19 changes: 17 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1932,10 +1932,21 @@ impl<'a> Parser<'a> {
| Keyword::BETWEEN
| Keyword::LIKE
| Keyword::ILIKE
| Keyword::SIMILAR => {
| Keyword::SIMILAR
| Keyword::REGEXP
| Keyword::RLIKE => {
self.prev_token();
let negated = self.parse_keyword(Keyword::NOT);
if self.parse_keyword(Keyword::IN) {
let regexp = self.parse_keyword(Keyword::REGEXP);
let rlike = self.parse_keyword(Keyword::RLIKE);
if regexp || rlike {
Ok(Expr::RLike {
negated,
expr: Box::new(expr),
pattern: Box::new(self.parse_subexpr(Self::LIKE_PREC)?),
regexp,
})
} else if self.parse_keyword(Keyword::IN) {
self.parse_in(expr, negated)
} else if self.parse_keyword(Keyword::BETWEEN) {
self.parse_between(expr, negated)
Expand Down Expand Up @@ -2178,6 +2189,8 @@ impl<'a> Parser<'a> {
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(Self::LIKE_PREC),
_ => Ok(0),
},
Expand All @@ -2186,6 +2199,8 @@ impl<'a> Parser<'a> {
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::LIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::ILIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::RLIKE => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::REGEXP => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::SIMILAR => Ok(Self::LIKE_PREC),
Token::Word(w) if w.keyword == Keyword::OPERATOR => Ok(Self::BETWEEN_PREC),
Token::Word(w) if w.keyword == Keyword::DIV => Ok(Self::MUL_DIV_MOD_OP_PREC),
Expand Down
2 changes: 1 addition & 1 deletion src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl TestedDialects {
/// 2. re-serializing the result of parsing `sql` produces the same
/// `canonical` sql string
pub fn one_statement_parses_to(&self, sql: &str, canonical: &str) -> Statement {
let mut statements = self.parse_sql_statements(sql).unwrap();
let mut statements = self.parse_sql_statements(sql).expect(sql);
assert_eq!(statements.len(), 1);

if !canonical.is_empty() && sql != canonical {
Expand Down
12 changes: 12 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,18 @@ fn parse_show_variables() {
mysql_and_generic().verified_stmt("SHOW VARIABLES WHERE value = '3306'");
}

#[test]
fn parse_rlike_and_regexp() {
for s in &[
"SELECT 1 WHERE 'a' RLIKE '^a$'",
"SELECT 1 WHERE 'a' REGEXP '^a$'",
"SELECT 1 WHERE 'a' NOT RLIKE '^a$'",
"SELECT 1 WHERE 'a' NOT REGEXP '^a$'",
] {
mysql_and_generic().verified_only_select(s);
}
}

#[test]
fn parse_kill() {
let stmt = mysql_and_generic().verified_stmt("KILL CONNECTION 5");
Expand Down

0 comments on commit c03586b

Please sign in to comment.