Skip to content

Commit e548d38

Browse files
authored
Support TRIM syntax (apache#331)
* Support TRIM syntax - Implement the following cases - TRIM([BOTH | LEADING | TRAILING] <expr> [FROM <expr>]) - TRIM(<expr>) * Resolve 1.54.0 clippy error - Remove needless borrow * Add test cases for TRIM
1 parent 67e17b2 commit e548d38

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

src/ast/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,14 @@ pub enum Expr {
219219
substring_from: Option<Box<Expr>>,
220220
substring_for: Option<Box<Expr>>,
221221
},
222+
/// TRIM([BOTH | LEADING | TRAILING] <expr> [FROM <expr>])\
223+
/// Or\
224+
/// TRIM(<expr>)
225+
Trim {
226+
expr: Box<Expr>,
227+
// ([BOTH | LEADING | TRAILING], <expr>)
228+
trim_where: Option<(Box<Ident>, Box<Expr>)>,
229+
},
222230
/// `expr COLLATE collation`
223231
Collate {
224232
expr: Box<Expr>,
@@ -361,6 +369,16 @@ impl fmt::Display for Expr {
361369
write!(f, " FOR {}", from_part)?;
362370
}
363371

372+
write!(f, ")")
373+
}
374+
Expr::Trim { expr, trim_where } => {
375+
write!(f, "TRIM(")?;
376+
if let Some((ident, trim_char)) = trim_where {
377+
write!(f, "{} {} FROM {}", ident, trim_char, expr)?;
378+
} else {
379+
write!(f, "{}", expr)?;
380+
}
381+
364382
write!(f, ")")
365383
}
366384
}

src/parser.rs

+26
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ impl<'a> Parser<'a> {
357357
Keyword::EXISTS => self.parse_exists_expr(),
358358
Keyword::EXTRACT => self.parse_extract_expr(),
359359
Keyword::SUBSTRING => self.parse_substring_expr(),
360+
Keyword::TRIM => self.parse_trim_expr(),
360361
Keyword::INTERVAL => self.parse_literal_interval(),
361362
Keyword::LISTAGG => self.parse_listagg_expr(),
362363
Keyword::NOT => Ok(Expr::UnaryOp {
@@ -647,6 +648,31 @@ impl<'a> Parser<'a> {
647648
})
648649
}
649650

651+
/// TRIM (WHERE 'text' FROM 'text')\
652+
/// TRIM ('text')
653+
pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> {
654+
self.expect_token(&Token::LParen)?;
655+
let mut where_expr = None;
656+
if let Token::Word(word) = self.peek_token() {
657+
if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING]
658+
.iter()
659+
.any(|d| word.keyword == *d)
660+
{
661+
let ident = self.parse_identifier()?;
662+
let sub_expr = self.parse_expr()?;
663+
self.expect_keyword(Keyword::FROM)?;
664+
where_expr = Some((ident, sub_expr))
665+
}
666+
}
667+
let expr = self.parse_expr()?;
668+
self.expect_token(&Token::RParen)?;
669+
670+
Ok(Expr::Trim {
671+
expr: Box::new(expr),
672+
trim_where: where_expr.map(|(ident, expr)| (Box::new(ident), Box::new(expr))),
673+
})
674+
}
675+
650676
/// Parse a SQL LISTAGG expression, e.g. `LISTAGG(...) WITHIN GROUP (ORDER BY ...)`.
651677
pub fn parse_listagg_expr(&mut self) -> Result<Expr, ParserError> {
652678
self.expect_token(&Token::LParen)?;

tests/sqlparser_common.rs

+20
Original file line numberDiff line numberDiff line change
@@ -2747,6 +2747,26 @@ fn parse_substring() {
27472747
one_statement_parses_to("SELECT SUBSTRING('1' FOR 3)", "SELECT SUBSTRING('1' FOR 3)");
27482748
}
27492749

2750+
#[test]
2751+
fn parse_trim() {
2752+
one_statement_parses_to(
2753+
"SELECT TRIM(BOTH 'xyz' FROM 'xyzfooxyz')",
2754+
"SELECT TRIM(BOTH 'xyz' FROM 'xyzfooxyz')",
2755+
);
2756+
2757+
one_statement_parses_to(
2758+
"SELECT TRIM(LEADING 'xyz' FROM 'xyzfooxyz')",
2759+
"SELECT TRIM(LEADING 'xyz' FROM 'xyzfooxyz')",
2760+
);
2761+
2762+
one_statement_parses_to(
2763+
"SELECT TRIM(TRAILING 'xyz' FROM 'xyzfooxyz')",
2764+
"SELECT TRIM(TRAILING 'xyz' FROM 'xyzfooxyz')",
2765+
);
2766+
2767+
one_statement_parses_to("SELECT TRIM(' foo ')", "SELECT TRIM(' foo ')");
2768+
}
2769+
27502770
#[test]
27512771
fn parse_exists_subquery() {
27522772
let expected_inner = verified_query("SELECT 1");

0 commit comments

Comments
 (0)