diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 36b17c30..b91aae43 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -331,13 +331,14 @@ pub enum Expr { substring_from: Option>, substring_for: Option>, }, - /// TRIM([BOTH | LEADING | TRAILING] [FROM ])\ + /// TRIM([BOTH | LEADING | TRAILING] [ FROM] )\ /// Or\ /// TRIM() Trim { expr: Box, - // ([BOTH | LEADING | TRAILING], ) - trim_where: Option<(TrimWhereField, Box)>, + // ([BOTH | LEADING | TRAILING] + trim_where: Option, + trim_what: Option>, }, /// `expr COLLATE collation` Collate { @@ -632,10 +633,17 @@ impl fmt::Display for Expr { } Expr::IsDistinctFrom(a, b) => write!(f, "{} IS 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(")?; - if let Some((ident, trim_char)) = trim_where { - write!(f, "{} {} FROM {}", ident, trim_char, expr)?; + if let Some(ident) = trim_where { + write!(f, "{} ", ident)?; + } + if let Some(trim_char) = trim_what { + write!(f, "{} FROM {}", trim_char, expr)?; } else { write!(f, "{}", expr)?; } diff --git a/src/parser.rs b/src/parser.rs index c803a3bb..3a90b3cc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -881,29 +881,37 @@ impl<'a> Parser<'a> { }) } - /// TRIM (WHERE 'text' FROM 'text')\ + /// TRIM ([WHERE] ['text' FROM] 'text')\ /// TRIM ('text') pub fn parse_trim_expr(&mut self) -> Result { self.expect_token(&Token::LParen)?; - let mut where_expr = None; + let mut trim_where = None; if let Token::Word(word) = self.peek_token() { if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING] .iter() .any(|d| word.keyword == *d) { - let trim_where = self.parse_trim_where()?; - let sub_expr = self.parse_expr()?; - self.expect_keyword(Keyword::FROM)?; - where_expr = Some((trim_where, Box::new(sub_expr))); + trim_where = Some(self.parse_trim_where()?); } } let expr = self.parse_expr()?; - self.expect_token(&Token::RParen)?; - - Ok(Expr::Trim { - expr: Box::new(expr), - trim_where: where_expr, - }) + if self.parse_keyword(Keyword::FROM) { + let trim_what = Box::new(expr); + let expr = self.parse_expr()?; + self.expect_token(&Token::RParen)?; + 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 { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index da8b544e..c87b0c5e 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3928,7 +3928,15 @@ fn parse_trim() { "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(LEADING ' foo ')", + "SELECT TRIM(LEADING ' foo ')", + ); assert_eq!( ParserError::ParserError("Expected ), found: 'xyz'".to_owned()),