diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1e430171e..97cc61935 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1131,7 +1131,7 @@ pub enum Expr { /// ```sql /// TRIM([BOTH | LEADING | TRAILING] [ FROM] ) /// TRIM() - /// TRIM(, [, characters]) -- only Snowflake or Bigquery + /// TRIM(, [, 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 6cef46067..8fca51518 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 ea4d7a971..87c762f0b 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 32967c4c5..e70efd695 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 a7a3c2715..1d5461fec 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 796b25f05..8703e402c 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 89b677c47..b99a8b5c3 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 984e384fd..a9d71fc4b 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 b44a1c5b8..39ee622d8 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 a00eab348..8d8b55a34 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(, [, characters]) -- only Snowflake or BigQuery + /// TRIM(, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic /// ``` pub fn parse_trim_expr(&mut self) -> Result { 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 982bf1088..8de460d78 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() );