From 1c5e4799af548291f6cf2af554a98d20c524b0ab Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sun, 5 May 2024 17:36:07 -0700 Subject: [PATCH] databricks except --- src/dialect/bigquery.rs | 5 +++++ src/dialect/clickhouse.rs | 4 ++++ src/dialect/databricks.rs | 5 +++++ src/dialect/generic.rs | 4 ++++ src/dialect/mod.rs | 10 ++++++++++ src/parser/mod.rs | 3 +-- tests/sqlparser_bigquery.rs | 32 -------------------------------- tests/sqlparser_common.rs | 34 ++++++++++++++++++++++++++++++++++ 8 files changed, 63 insertions(+), 34 deletions(-) diff --git a/src/dialect/bigquery.rs b/src/dialect/bigquery.rs index c7f579826..d65de3a47 100644 --- a/src/dialect/bigquery.rs +++ b/src/dialect/bigquery.rs @@ -54,4 +54,9 @@ impl Dialect for BigQueryDialect { fn supports_parenthesized_set_variables(&self) -> bool { true } + + // See https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_except + fn supports_select_wildcard_except(&self) -> bool { + true + } } diff --git a/src/dialect/clickhouse.rs b/src/dialect/clickhouse.rs index 83cc4ae9a..155b827e1 100644 --- a/src/dialect/clickhouse.rs +++ b/src/dialect/clickhouse.rs @@ -29,4 +29,8 @@ impl Dialect for ClickHouseDialect { fn supports_string_literal_backslash_escape(&self) -> bool { true } + + fn supports_select_wildcard_except(&self) -> bool { + true + } } diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs index 929ec26cd..42d432d3d 100644 --- a/src/dialect/databricks.rs +++ b/src/dialect/databricks.rs @@ -33,4 +33,9 @@ impl Dialect for DatabricksDialect { fn supports_lambda_functions(&self) -> bool { true } + + // https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select.html#syntax + fn supports_select_wildcard_except(&self) -> bool { + true + } } diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index 613abfe41..33391d479 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -66,4 +66,8 @@ impl Dialect for GenericDialect { fn supports_parenthesized_set_variables(&self) -> bool { true } + + fn supports_select_wildcard_except(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index e737bf399..da5c8c5ac 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -226,6 +226,16 @@ pub trait Dialect: Debug + Any { fn supports_parenthesized_set_variables(&self) -> bool { false } + /// Returns true if the dialect supports an `EXCEPT` clause following a + /// wildcard in a select list. + /// + /// For example + /// ```sql + /// SELECT * EXCEPT order_id FROM orders; + /// ``` + fn supports_select_wildcard_except(&self) -> bool { + false + } /// Returns true if the dialect has a CONVERT function which accepts a type first /// and an expression second, e.g. `CONVERT(varchar, 1)` fn convert_type_before_value(&self) -> bool { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index dc416bb7e..a7ec4d093 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9704,8 +9704,7 @@ impl<'a> Parser<'a> { } else { None }; - let opt_except = if dialect_of!(self is GenericDialect | BigQueryDialect | ClickHouseDialect) - { + let opt_except = if self.dialect.supports_select_wildcard_except() { self.parse_optional_select_item_except()? } else { None diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 867323553..179755e0c 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1838,38 +1838,6 @@ fn parse_array_agg_func() { } } -#[test] -fn test_select_wildcard_with_except() { - let select = bigquery_and_generic().verified_only_select("SELECT * EXCEPT (col_a) FROM data"); - let expected = SelectItem::Wildcard(WildcardAdditionalOptions { - opt_except: Some(ExceptSelectItem { - first_element: Ident::new("col_a"), - additional_elements: vec![], - }), - ..Default::default() - }); - assert_eq!(expected, select.projection[0]); - - let select = bigquery_and_generic() - .verified_only_select("SELECT * EXCEPT (department_id, employee_id) FROM employee_table"); - let expected = SelectItem::Wildcard(WildcardAdditionalOptions { - opt_except: Some(ExceptSelectItem { - first_element: Ident::new("department_id"), - additional_elements: vec![Ident::new("employee_id")], - }), - ..Default::default() - }); - assert_eq!(expected, select.projection[0]); - - assert_eq!( - bigquery_and_generic() - .parse_sql_statements("SELECT * EXCEPT () FROM employee_table") - .unwrap_err() - .to_string(), - "sql parser error: Expected identifier, found: )" - ); -} - #[test] fn parse_big_query_declare() { for (sql, expected_names, expected_data_type, expected_assigned_expr) in [ diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 142b359a4..6668ce8f4 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -9854,3 +9854,37 @@ fn tests_select_values_without_parens_and_set_op() { _ => panic!("Expected a SET OPERATION"), } } + +#[test] +fn parse_select_wildcard_with_except() { + let dialects = all_dialects_where(|d| d.supports_select_wildcard_except()); + + let select = dialects.verified_only_select("SELECT * EXCEPT (col_a) FROM data"); + let expected = SelectItem::Wildcard(WildcardAdditionalOptions { + opt_except: Some(ExceptSelectItem { + first_element: Ident::new("col_a"), + additional_elements: vec![], + }), + ..Default::default() + }); + assert_eq!(expected, select.projection[0]); + + let select = dialects + .verified_only_select("SELECT * EXCEPT (department_id, employee_id) FROM employee_table"); + let expected = SelectItem::Wildcard(WildcardAdditionalOptions { + opt_except: Some(ExceptSelectItem { + first_element: Ident::new("department_id"), + additional_elements: vec![Ident::new("employee_id")], + }), + ..Default::default() + }); + assert_eq!(expected, select.projection[0]); + + assert_eq!( + dialects + .parse_sql_statements("SELECT * EXCEPT () FROM employee_table") + .unwrap_err() + .to_string(), + "sql parser error: Expected identifier, found: )" + ); +}