Skip to content

Commit da201d4

Browse files
committed
snowflake: handle like ... escape ...
1 parent 495465d commit da201d4

4 files changed

Lines changed: 76 additions & 18 deletions

File tree

src/ast/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ pub enum Expr {
184184
low: Box<Expr>,
185185
high: Box<Expr>,
186186
},
187+
/// `<expr> [I]LIKE <pattern> [ ESCAPE <escape> ]`
188+
Like {
189+
expr: Box<Expr>,
190+
case_sensitive: bool,
191+
negated: bool,
192+
pat: Box<Expr>,
193+
esc: Option<Box<Expr>>,
194+
},
187195
/// Binary operation e.g. `1 + 1` or `foo > bar`
188196
BinaryOp {
189197
left: Box<Expr>,
@@ -286,6 +294,27 @@ impl fmt::Display for Expr {
286294
low,
287295
high
288296
),
297+
Expr::Like {
298+
expr,
299+
case_sensitive,
300+
negated,
301+
pat,
302+
esc,
303+
} => {
304+
write!(
305+
f,
306+
"{} {}{}LIKE {}",
307+
expr,
308+
if *negated { "NOT " } else { "" },
309+
if *case_sensitive { "" } else { "I" },
310+
pat,
311+
)?;
312+
if let Some(esc) = esc {
313+
write!(f, "ESCAPE {}", esc)
314+
} else {
315+
Ok(())
316+
}
317+
}
289318
Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right),
290319
Expr::UnaryOp { op, expr } => {
291320
if op == &UnaryOperator::PGPostfixFactorial {

src/ast/operator.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,8 @@ pub enum BinaryOperator {
6969
NotEq,
7070
And,
7171
Or,
72-
Ilike,
73-
Like,
74-
NotLike,
75-
NotIlike,
72+
Rlike,
73+
NotRlike,
7674
JsonIndex,
7775
BitwiseOr,
7876
BitwiseAnd,
@@ -99,10 +97,8 @@ impl fmt::Display for BinaryOperator {
9997
BinaryOperator::NotEq => "<>",
10098
BinaryOperator::And => "AND",
10199
BinaryOperator::Or => "OR",
102-
BinaryOperator::Like => "LIKE",
103-
BinaryOperator::Ilike => "ILIKE",
104-
BinaryOperator::NotLike => "NOT LIKE",
105-
BinaryOperator::NotIlike => "NOT ILIKE",
100+
BinaryOperator::Rlike => "RLIKE",
101+
BinaryOperator::NotRlike => "NOT RLIKE",
106102
BinaryOperator::JsonIndex => ":",
107103
BinaryOperator::BitwiseOr => "|",
108104
BinaryOperator::BitwiseAnd => "&",

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ define_keywords!(
363363
RETURNS,
364364
REVOKE,
365365
RIGHT,
366+
RLIKE,
366367
ROLLBACK,
367368
ROLLUP,
368369
ROW,

src/parser.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -729,13 +729,10 @@ impl<'a> Parser<'a> {
729729
Token::Word(w) => match w.keyword {
730730
Keyword::AND => Some(BinaryOperator::And),
731731
Keyword::OR => Some(BinaryOperator::Or),
732-
Keyword::LIKE => Some(BinaryOperator::Like),
733-
Keyword::ILIKE => Some(BinaryOperator::Ilike),
732+
Keyword::RLIKE => Some(BinaryOperator::Rlike),
734733
Keyword::NOT => {
735-
if self.parse_keyword(Keyword::LIKE) {
736-
Some(BinaryOperator::NotLike)
737-
} else if self.parse_keyword(Keyword::ILIKE) {
738-
Some(BinaryOperator::NotIlike)
734+
if self.parse_keyword(Keyword::RLIKE) {
735+
Some(BinaryOperator::NotRlike)
739736
} else {
740737
None
741738
}
@@ -762,15 +759,19 @@ impl<'a> Parser<'a> {
762759
self.expected("NULL or NOT NULL after IS", self.peek_token())
763760
}
764761
}
765-
Keyword::NOT | Keyword::IN | Keyword::BETWEEN => {
762+
Keyword::NOT | Keyword::IN | Keyword::BETWEEN | Keyword::LIKE | Keyword::ILIKE => {
766763
self.prev_token();
767764
let negated = self.parse_keyword(Keyword::NOT);
768765
if self.parse_keyword(Keyword::IN) {
769766
self.parse_in(expr, negated)
770767
} else if self.parse_keyword(Keyword::BETWEEN) {
771768
self.parse_between(expr, negated)
769+
} else if self.parse_keyword(Keyword::LIKE) {
770+
self.parse_like(expr, true, negated)
771+
} else if self.parse_keyword(Keyword::ILIKE) {
772+
self.parse_like(expr, false, negated)
772773
} else {
773-
self.expected("IN or BETWEEN after NOT", self.peek_token())
774+
self.expected("IN or BETWEEN or [I]LIKE after NOT", self.peek_token())
774775
}
775776
}
776777
// Can only happen if `get_next_precedence` got out of sync with this function
@@ -826,6 +827,29 @@ impl<'a> Parser<'a> {
826827
})
827828
}
828829

830+
/// Parses [I]LIKE <pattern> [ ESCAPE <escape> ]
831+
/// https://docs.snowflake.com/en/sql-reference/functions/ilike.html
832+
pub fn parse_like(
833+
&mut self,
834+
expr: Expr,
835+
case_sensitive: bool,
836+
negated: bool,
837+
) -> Result<Expr, ParserError> {
838+
let pat = self.parse_expr()?;
839+
let esc = if self.parse_keyword(Keyword::ESCAPE) {
840+
Some(self.parse_expr()?)
841+
} else {
842+
None
843+
};
844+
Ok(Expr::Like {
845+
expr: Box::new(expr),
846+
case_sensitive,
847+
negated,
848+
pat: Box::new(pat),
849+
esc: esc.map(Box::new),
850+
})
851+
}
852+
829853
/// Parse a postgresql casting style which is in the form of `expr::datatype`
830854
pub fn parse_pg_cast(&mut self, expr: Expr) -> Result<Expr, ParserError> {
831855
Ok(Expr::Cast {
@@ -853,15 +877,23 @@ impl<'a> Parser<'a> {
853877
// precedence.
854878
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
855879
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
856-
Token::Word(w) if w.keyword == Keyword::LIKE || w.keyword == Keyword::ILIKE => {
880+
Token::Word(w)
881+
if w.keyword == Keyword::LIKE
882+
|| w.keyword == Keyword::ILIKE
883+
|| w.keyword == Keyword::RLIKE =>
884+
{
857885
Ok(Self::BETWEEN_PREC)
858886
}
859887
_ => Ok(0),
860888
},
861889
Token::Word(w) if w.keyword == Keyword::IS => Ok(17),
862890
Token::Word(w) if w.keyword == Keyword::IN => Ok(Self::BETWEEN_PREC),
863891
Token::Word(w) if w.keyword == Keyword::BETWEEN => Ok(Self::BETWEEN_PREC),
864-
Token::Word(w) if w.keyword == Keyword::LIKE || w.keyword == Keyword::ILIKE => {
892+
Token::Word(w)
893+
if w.keyword == Keyword::LIKE
894+
|| w.keyword == Keyword::ILIKE
895+
|| w.keyword == Keyword::RLIKE =>
896+
{
865897
Ok(Self::BETWEEN_PREC)
866898
}
867899
Token::Eq | Token::Lt | Token::LtEq | Token::Neq | Token::Gt | Token::GtEq => Ok(20),

0 commit comments

Comments
 (0)