This is an automated email from the ASF dual-hosted git repository. github-bot pushed a commit to branch gh-readonly-queue/main/pr-2240-83baf5e89179dd00321330312fe460100d755c25 in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 5b7bc1a52723cc91d4d0b90a44455d716db549d5 Author: Luca Cappelletti <[email protected]> AuthorDate: Fri Feb 27 13:38:54 2026 +0100 Support two-argument `TRIM(string, characters)` in PostgreSQL (#2240) --- src/ast/mod.rs | 2 +- src/dialect/bigquery.rs | 4 ++++ src/dialect/clickhouse.rs | 4 ++++ src/dialect/duckdb.rs | 4 ++++ src/dialect/generic.rs | 4 ++++ src/dialect/mod.rs | 6 ++++++ src/dialect/postgresql.rs | 4 ++++ src/dialect/snowflake.rs | 4 ++++ src/dialect/sqlite.rs | 4 ++++ src/parser/mod.rs | 5 ++--- tests/sqlparser_common.rs | 53 +++++++++++++++++++++++++++++++++-------------- 11 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1e430171..97cc6193 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1131,7 +1131,7 @@ pub enum Expr { /// ```sql /// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>) /// TRIM(<expr>) - /// TRIM(<expr>, [, characters]) -- only Snowflake or Bigquery + /// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic /// ``` Trim { /// The expression to trim from. diff --git a/src/dialect/bigquery.rs b/src/dialect/bigquery.rs index 6cef4606..8fca5151 100644 --- a/src/dialect/bigquery.rs +++ b/src/dialect/bigquery.rs @@ -162,4 +162,8 @@ impl Dialect for BigQueryDialect { fn supports_select_wildcard_replace(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/dialect/clickhouse.rs b/src/dialect/clickhouse.rs index ea4d7a97..87c762f0 100644 --- a/src/dialect/clickhouse.rs +++ b/src/dialect/clickhouse.rs @@ -141,4 +141,8 @@ impl Dialect for ClickHouseDialect { fn supports_select_wildcard_replace(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/dialect/duckdb.rs b/src/dialect/duckdb.rs index 32967c4c..e70efd69 100644 --- a/src/dialect/duckdb.rs +++ b/src/dialect/duckdb.rs @@ -129,4 +129,8 @@ impl Dialect for DuckDbDialect { fn supports_select_wildcard_replace(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index a7a3c271..1d5461fe 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -284,4 +284,8 @@ impl Dialect for GenericDialect { fn supports_key_column_option(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 796b25f0..8703e402 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1651,6 +1651,12 @@ pub trait Dialect: Debug + Any { fn supports_select_format(&self) -> bool { false } + + /// Returns true if the dialect supports the two-argument comma-separated + /// form of the `TRIM` function: `TRIM(expr, characters)`. + fn supports_comma_separated_trim(&self) -> bool { + false + } } /// Operators for which precedence must be defined. diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index 89b677c4..b99a8b5c 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -306,4 +306,8 @@ impl Dialect for PostgreSqlDialect { fn supports_create_table_like_parenthesized(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 984e384f..a9d71fc4 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -667,6 +667,10 @@ impl Dialect for SnowflakeDialect { fn supports_lambda_functions(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } // Peeks ahead to identify tokens that are expected after diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs index b44a1c5b..39ee622d 100644 --- a/src/dialect/sqlite.rs +++ b/src/dialect/sqlite.rs @@ -120,4 +120,8 @@ impl Dialect for SQLiteDialect { fn supports_notnull_operator(&self) -> bool { true } + + fn supports_comma_separated_trim(&self) -> bool { + true + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a00eab34..8d8b55a3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2940,7 +2940,7 @@ impl<'a> Parser<'a> { /// ```sql /// TRIM ([WHERE] ['text' FROM] 'text') /// TRIM ('text') - /// TRIM(<expr>, [, characters]) -- only Snowflake or BigQuery + /// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic /// ``` pub fn parse_trim_expr(&mut self) -> Result<Expr, ParserError> { self.expect_token(&Token::LParen)?; @@ -2961,8 +2961,7 @@ impl<'a> Parser<'a> { trim_what: Some(trim_what), trim_characters: None, }) - } else if self.consume_token(&Token::Comma) - && dialect_of!(self is DuckDbDialect | SnowflakeDialect | BigQueryDialect | GenericDialect) + } else if self.dialect.supports_comma_separated_trim() && self.consume_token(&Token::Comma) { let characters = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 982bf108..8de460d7 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -8099,23 +8099,46 @@ fn parse_trim() { parse_sql_statements("SELECT TRIM(FOO 'xyz' FROM 'xyzfooxyz')").unwrap_err() ); - //keep Snowflake/BigQuery TRIM syntax failing - let all_expected_snowflake = TestedDialects::new(vec![ - //Box::new(GenericDialect {}), - Box::new(PostgreSqlDialect {}), - Box::new(MsSqlDialect {}), - Box::new(AnsiDialect {}), - //Box::new(SnowflakeDialect {}), - Box::new(HiveDialect {}), - Box::new(RedshiftSqlDialect {}), - Box::new(MySqlDialect {}), - //Box::new(BigQueryDialect {}), - Box::new(SQLiteDialect {}), - ]); + // dialects that support comma-separated TRIM syntax + let dialects = all_dialects_where(|d| d.supports_comma_separated_trim()); + let sql = "SELECT TRIM(' xyz ', ' ')"; + let select = dialects.verified_only_select(sql); assert_eq!( - ParserError::ParserError("Expected: ), found: 'a'".to_owned()), - all_expected_snowflake + &Expr::Trim { + expr: Box::new(Expr::Value( + Value::SingleQuotedString(" xyz ".to_owned()).with_empty_span() + )), + trim_where: None, + trim_what: None, + trim_characters: Some(vec![Expr::Value( + Value::SingleQuotedString(" ".to_owned()).with_empty_span() + )]), + }, + expr_from_projection(only(&select.projection)) + ); + + let sql = "SELECT TRIM('xyz', 'a')"; + let select = dialects.verified_only_select(sql); + assert_eq!( + &Expr::Trim { + expr: Box::new(Expr::Value( + Value::SingleQuotedString("xyz".to_owned()).with_empty_span() + )), + trim_where: None, + trim_what: None, + trim_characters: Some(vec![Expr::Value( + Value::SingleQuotedString("a".to_owned()).with_empty_span() + )]), + }, + expr_from_projection(only(&select.projection)) + ); + + // dialects without comma-style TRIM syntax should fail + let unsupported_dialects = all_dialects_where(|d| !d.supports_comma_separated_trim()); + assert_eq!( + ParserError::ParserError("Expected: ), found: ,".to_owned()), + unsupported_dialects .parse_sql_statements("SELECT TRIM('xyz', 'a')") .unwrap_err() ); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
