Add support for NULL escape char in pattern match searches

This commit is contained in:
Yoav Cohen 2025-06-29 21:45:20 +02:00
parent 3bc94234df
commit 7805f333d6
3 changed files with 29 additions and 12 deletions

View file

@ -809,7 +809,7 @@ pub enum Expr {
any: bool,
expr: Box<Expr>,
pattern: Box<Expr>,
escape_char: Option<String>,
escape_char: Option<Value>,
},
/// `ILIKE` (case-insensitive `LIKE`)
ILike {
@ -819,14 +819,14 @@ pub enum Expr {
any: bool,
expr: Box<Expr>,
pattern: Box<Expr>,
escape_char: Option<String>,
escape_char: Option<Value>,
},
/// SIMILAR TO regex
SimilarTo {
negated: bool,
expr: Box<Expr>,
pattern: Box<Expr>,
escape_char: Option<String>,
escape_char: Option<Value>,
},
/// MySQL: RLIKE regex or REGEXP regex
RLike {
@ -1486,7 +1486,7 @@ impl fmt::Display for Expr {
} => match escape_char {
Some(ch) => write!(
f,
"{} {}LIKE {}{} ESCAPE '{}'",
"{} {}LIKE {}{} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY " } else { "" },
@ -1511,7 +1511,7 @@ impl fmt::Display for Expr {
} => match escape_char {
Some(ch) => write!(
f,
"{} {}ILIKE {}{} ESCAPE '{}'",
"{} {}ILIKE {}{} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
if *any { "ANY" } else { "" },
@ -1566,7 +1566,7 @@ impl fmt::Display for Expr {
} => match escape_char {
Some(ch) => write!(
f,
"{} {}SIMILAR TO {} ESCAPE '{}'",
"{} {}SIMILAR TO {} ESCAPE {}",
expr,
if *negated { "NOT " } else { "" },
pattern,

View file

@ -3642,9 +3642,9 @@ impl<'a> Parser<'a> {
}
/// Parse the `ESCAPE CHAR` portion of `LIKE`, `ILIKE`, and `SIMILAR TO`
pub fn parse_escape_char(&mut self) -> Result<Option<String>, ParserError> {
pub fn parse_escape_char(&mut self) -> Result<Option<Value>, ParserError> {
if self.parse_keyword(Keyword::ESCAPE) {
Ok(Some(self.parse_literal_string()?))
Ok(Some(self.parse_value()?.into()))
} else {
Ok(None)
}

View file

@ -2040,7 +2040,7 @@ fn parse_ilike() {
pattern: Box::new(Expr::Value(
(Value::SingleQuotedString("%a".to_string())).with_empty_span()
)),
escape_char: Some('^'.to_string()),
escape_char: Some(Value::SingleQuotedString('^'.to_string())),
any: false,
},
select.selection.unwrap()
@ -2104,7 +2104,7 @@ fn parse_like() {
pattern: Box::new(Expr::Value(
(Value::SingleQuotedString("%a".to_string())).with_empty_span()
)),
escape_char: Some('^'.to_string()),
escape_char: Some(Value::SingleQuotedString('^'.to_string())),
any: false,
},
select.selection.unwrap()
@ -2167,7 +2167,24 @@ fn parse_similar_to() {
pattern: Box::new(Expr::Value(
(Value::SingleQuotedString("%a".to_string())).with_empty_span()
)),
escape_char: Some('^'.to_string()),
escape_char: Some(Value::SingleQuotedString('^'.to_string())),
},
select.selection.unwrap()
);
let sql = &format!(
"SELECT * FROM customers WHERE name {}SIMILAR TO '%a' ESCAPE NULL",
if negated { "NOT " } else { "" }
);
let select = verified_only_select(sql);
assert_eq!(
Expr::SimilarTo {
expr: Box::new(Expr::Identifier(Ident::new("name"))),
negated,
pattern: Box::new(Expr::Value(
(Value::SingleQuotedString("%a".to_string())).with_empty_span()
)),
escape_char: Some(Value::Null),
},
select.selection.unwrap()
);
@ -2185,7 +2202,7 @@ fn parse_similar_to() {
pattern: Box::new(Expr::Value(
(Value::SingleQuotedString("%a".to_string())).with_empty_span()
)),
escape_char: Some('^'.to_string()),
escape_char: Some(Value::SingleQuotedString('^'.to_string())),
})),
select.selection.unwrap()
);