Skip to content

Commit 868ca05

Browse files
committed
pg/rs: SIMILAR TO
1 parent 4355c2e commit 868ca05

2 files changed

Lines changed: 57 additions & 3 deletions

File tree

src/ast/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ pub enum Expr {
192192
pat: Box<Expr>,
193193
esc: Option<Box<Expr>>,
194194
},
195+
/// `<expr> SIMILAR TO <pattern> [ ESCAPE <escape> ]`
196+
Similar {
197+
expr: Box<Expr>,
198+
pat: Box<Expr>,
199+
negated: bool,
200+
esc: Option<Box<Expr>>,
201+
},
195202
/// Binary operation e.g. `1 + 1` or `foo > bar`
196203
BinaryOp {
197204
left: Box<Expr>,
@@ -332,6 +339,25 @@ impl fmt::Display for Expr {
332339
Ok(())
333340
}
334341
}
342+
Expr::Similar {
343+
expr,
344+
negated,
345+
pat,
346+
esc,
347+
} => {
348+
write!(
349+
f,
350+
"{} {}SIMILAR TO {}",
351+
expr,
352+
if *negated { "NOT " } else { "" },
353+
pat,
354+
)?;
355+
if let Some(esc) = esc {
356+
write!(f, "ESCAPE {}", esc)
357+
} else {
358+
Ok(())
359+
}
360+
}
335361
Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right),
336362
Expr::UnaryOp { op, expr } => {
337363
if op == &UnaryOperator::PGPostfixFactorial {

src/parser.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,12 @@ impl<'a> Parser<'a> {
846846
self.expected("NULL or NOT NULL after IS", self.peek_token())
847847
}
848848
}
849-
Keyword::NOT | Keyword::IN | Keyword::BETWEEN | Keyword::LIKE | Keyword::ILIKE => {
849+
Keyword::NOT
850+
| Keyword::IN
851+
| Keyword::BETWEEN
852+
| Keyword::LIKE
853+
| Keyword::ILIKE
854+
| Keyword::SIMILAR => {
850855
self.prev_token();
851856
// allow backtracking if parsing IN doesn't work
852857
// https://docs.snowflake.com/en/sql-reference/functions/position.html
@@ -865,8 +870,13 @@ impl<'a> Parser<'a> {
865870
Ok((self.parse_like(expr, true, negated)?, true))
866871
} else if self.parse_keyword(Keyword::ILIKE) {
867872
Ok((self.parse_like(expr, false, negated)?, true))
873+
} else if self.parse_keywords(&[Keyword::SIMILAR, Keyword::TO]) {
874+
Ok((self.parse_similar(expr, negated)?, true))
868875
} else {
869-
self.expected("IN or BETWEEN or [I]LIKE after NOT", self.peek_token())
876+
self.expected(
877+
"IN or BETWEEN or [I]LIKE or SIMILAR TO after NOT",
878+
self.peek_token(),
879+
)
870880
}
871881
}
872882
// Can only happen if `get_next_precedence` got out of sync with this function
@@ -951,6 +961,23 @@ impl<'a> Parser<'a> {
951961
})
952962
}
953963

964+
/// Parses SIMILAR TO <pattern> [ ESCAPE <escape> ]
965+
/// https://www.postgresql.org/docs/9.0/functions-matching.html#FUNCTIONS-SIMILARTO-REGEXP
966+
pub fn parse_similar(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> {
967+
let pat = self.parse_expr()?;
968+
let esc = if self.parse_keyword(Keyword::ESCAPE) {
969+
Some(self.parse_expr()?)
970+
} else {
971+
None
972+
};
973+
Ok(Expr::Similar {
974+
expr: Box::new(expr),
975+
pat: Box::new(pat),
976+
negated,
977+
esc: esc.map(Box::new),
978+
})
979+
}
980+
954981
/// Parse a postgresql casting style which is in the form of `expr::datatype`
955982
pub fn parse_pg_cast(&mut self, expr: Expr) -> Result<Expr, ParserError> {
956983
Ok(Expr::Cast {
@@ -998,7 +1025,8 @@ impl<'a> Parser<'a> {
9981025
Token::Word(w)
9991026
if w.keyword == Keyword::LIKE
10001027
|| w.keyword == Keyword::ILIKE
1001-
|| w.keyword == Keyword::RLIKE =>
1028+
|| w.keyword == Keyword::RLIKE
1029+
|| w.keyword == Keyword::SIMILAR =>
10021030
{
10031031
Ok(Self::BETWEEN_PREC)
10041032
}

0 commit comments

Comments
 (0)