Support TRIM from with optional FROM clause (#573)

* Split trimwhereFlag and trim_char expr to seperate members of Expr::Trim

* update trim parsing logic to handle no flag + char from expr combo

* add tests

* Allow trim flag without trim_what expr

* Add trim flag without trim_what test
This commit is contained in:
Ayush Dattagupta 2022-08-15 14:58:43 -07:00 committed by GitHub
parent 31ba0012f7
commit 42c5d43b45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 18 deletions

View file

@ -331,13 +331,14 @@ pub enum Expr {
substring_from: Option<Box<Expr>>, substring_from: Option<Box<Expr>>,
substring_for: Option<Box<Expr>>, substring_for: Option<Box<Expr>>,
}, },
/// TRIM([BOTH | LEADING | TRAILING] <expr> [FROM <expr>])\ /// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)\
/// Or\ /// Or\
/// TRIM(<expr>) /// TRIM(<expr>)
Trim { Trim {
expr: Box<Expr>, expr: Box<Expr>,
// ([BOTH | LEADING | TRAILING], <expr>) // ([BOTH | LEADING | TRAILING]
trim_where: Option<(TrimWhereField, Box<Expr>)>, trim_where: Option<TrimWhereField>,
trim_what: Option<Box<Expr>>,
}, },
/// `expr COLLATE collation` /// `expr COLLATE collation`
Collate { Collate {
@ -632,10 +633,17 @@ impl fmt::Display for Expr {
} }
Expr::IsDistinctFrom(a, b) => write!(f, "{} IS DISTINCT FROM {}", a, b), Expr::IsDistinctFrom(a, b) => write!(f, "{} IS DISTINCT FROM {}", a, b),
Expr::IsNotDistinctFrom(a, b) => write!(f, "{} IS NOT DISTINCT FROM {}", a, b), Expr::IsNotDistinctFrom(a, b) => write!(f, "{} IS NOT DISTINCT FROM {}", a, b),
Expr::Trim { expr, trim_where } => { Expr::Trim {
expr,
trim_where,
trim_what,
} => {
write!(f, "TRIM(")?; write!(f, "TRIM(")?;
if let Some((ident, trim_char)) = trim_where { if let Some(ident) = trim_where {
write!(f, "{} {} FROM {}", ident, trim_char, expr)?; write!(f, "{} ", ident)?;
}
if let Some(trim_char) = trim_what {
write!(f, "{} FROM {}", trim_char, expr)?;
} else { } else {
write!(f, "{}", expr)?; write!(f, "{}", expr)?;
} }

View file

@ -881,29 +881,37 @@ impl<'a> Parser<'a> {
}) })
} }
/// TRIM (WHERE 'text' FROM 'text')\ /// TRIM ([WHERE] ['text' FROM] 'text')\
/// TRIM ('text') /// TRIM ('text')
pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> { pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> {
self.expect_token(&Token::LParen)?; self.expect_token(&Token::LParen)?;
let mut where_expr = None; let mut trim_where = None;
if let Token::Word(word) = self.peek_token() { if let Token::Word(word) = self.peek_token() {
if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING] if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING]
.iter() .iter()
.any(|d| word.keyword == *d) .any(|d| word.keyword == *d)
{ {
let trim_where = self.parse_trim_where()?; trim_where = Some(self.parse_trim_where()?);
let sub_expr = self.parse_expr()?;
self.expect_keyword(Keyword::FROM)?;
where_expr = Some((trim_where, Box::new(sub_expr)));
} }
} }
let expr = self.parse_expr()?; let expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?; if self.parse_keyword(Keyword::FROM) {
let trim_what = Box::new(expr);
Ok(Expr::Trim { let expr = self.parse_expr()?;
expr: Box::new(expr), self.expect_token(&Token::RParen)?;
trim_where: where_expr, Ok(Expr::Trim {
}) expr: Box::new(expr),
trim_where,
trim_what: Some(trim_what),
})
} else {
self.expect_token(&Token::RParen)?;
Ok(Expr::Trim {
expr: Box::new(expr),
trim_where,
trim_what: None,
})
}
} }
pub fn parse_trim_where(&mut self) -> Result<TrimWhereField, ParserError> { pub fn parse_trim_where(&mut self) -> Result<TrimWhereField, ParserError> {

View file

@ -3928,7 +3928,15 @@ fn parse_trim() {
"SELECT TRIM(TRAILING 'xyz' FROM 'xyzfooxyz')", "SELECT TRIM(TRAILING 'xyz' FROM 'xyzfooxyz')",
); );
one_statement_parses_to(
"SELECT TRIM('xyz' FROM 'xyzfooxyz')",
"SELECT TRIM('xyz' FROM 'xyzfooxyz')",
);
one_statement_parses_to("SELECT TRIM(' foo ')", "SELECT TRIM(' foo ')"); one_statement_parses_to("SELECT TRIM(' foo ')", "SELECT TRIM(' foo ')");
one_statement_parses_to(
"SELECT TRIM(LEADING ' foo ')",
"SELECT TRIM(LEADING ' foo ')",
);
assert_eq!( assert_eq!(
ParserError::ParserError("Expected ), found: 'xyz'".to_owned()), ParserError::ParserError("Expected ), found: 'xyz'".to_owned()),