diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs index 029709fea..40807a016 100644 --- a/src/dialect/databricks.rs +++ b/src/dialect/databricks.rs @@ -79,4 +79,9 @@ impl Dialect for DatabricksDialect { fn supports_group_by_with_modifier(&self) -> bool { true } + + /// See + fn supports_values_as_table_factor(&self) -> bool { + true + } } diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index da57253d6..42510e2f0 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -104,6 +104,22 @@ impl Dialect for GenericDialect { true } + fn supports_extract_comma_syntax(&self) -> bool { + true + } + + fn supports_create_view_comment_syntax(&self) -> bool { + true + } + + fn supports_parens_around_table_factor(&self) -> bool { + true + } + + fn supports_values_as_table_factor(&self) -> bool { + true + } + fn supports_create_index_with_clause(&self) -> bool { true } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index cd7fdee12..284fc4172 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -856,6 +856,87 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports the `EXTRACT` function + /// with a comma separator instead of `FROM`. + /// + /// Example: + /// ```sql + /// SELECT EXTRACT(YEAR, date_column) FROM table; + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/extract) + fn supports_extract_comma_syntax(&self) -> bool { + false + } + + /// Returns true if this dialect supports a subquery passed to a function + /// as the only argument without enclosing parentheses. + /// + /// Example: + /// ```sql + /// SELECT FLATTEN(SELECT * FROM tbl); + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/functions/flatten) + fn supports_subquery_as_function_arg(&self) -> bool { + false + } + + /// Returns true if this dialect supports the `COMMENT` clause in + /// `CREATE VIEW` statements using the `COMMENT = 'comment'` syntax. + /// + /// Example: + /// ```sql + /// CREATE VIEW v COMMENT = 'my comment' AS SELECT 1; + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/create-view#optional-parameters) + fn supports_create_view_comment_syntax(&self) -> bool { + false + } + + /// Returns true if this dialect supports the `ARRAY` type without + /// specifying an element type. + /// + /// Example: + /// ```sql + /// CREATE TABLE t (a ARRAY); + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/data-types-semistructured#array) + fn supports_array_typedef_without_element_type(&self) -> bool { + false + } + + /// Returns true if this dialect supports extra parentheses around + /// lone table names or derived tables in the `FROM` clause. + /// + /// Example: + /// ```sql + /// SELECT * FROM (mytable); + /// SELECT * FROM ((SELECT 1)); + /// SELECT * FROM (mytable) AS alias; + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/from) + fn supports_parens_around_table_factor(&self) -> bool { + false + } + + /// Returns true if this dialect supports `VALUES` as a table factor + /// without requiring parentheses around the entire clause. + /// + /// Example: + /// ```sql + /// SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2); + /// ``` + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/constructs/values) + /// [Databricks](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-values.html) + fn supports_values_as_table_factor(&self) -> bool { + false + } + /// Returns true if this dialect allows dollar placeholders /// e.g. `SELECT $var` (SQLite) fn supports_dollar_placeholder(&self) -> bool { diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 1e571d0f8..d768f7a21 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -211,6 +211,36 @@ impl Dialect for SnowflakeDialect { true } + /// See [doc](https://docs.snowflake.com/en/sql-reference/functions/extract) + fn supports_extract_comma_syntax(&self) -> bool { + true + } + + /// See [doc](https://docs.snowflake.com/en/sql-reference/functions/flatten) + fn supports_subquery_as_function_arg(&self) -> bool { + true + } + + /// See [doc](https://docs.snowflake.com/en/sql-reference/sql/create-view#optional-parameters) + fn supports_create_view_comment_syntax(&self) -> bool { + true + } + + /// See [doc](https://docs.snowflake.com/en/sql-reference/data-types-semistructured#array) + fn supports_array_typedef_without_element_type(&self) -> bool { + true + } + + /// See [doc](https://docs.snowflake.com/en/sql-reference/constructs/from) + fn supports_parens_around_table_factor(&self) -> bool { + true + } + + /// See [doc](https://docs.snowflake.com/en/sql-reference/constructs/values) + fn supports_values_as_table_factor(&self) -> bool { + true + } + fn parse_statement(&self, parser: &mut Parser) -> Option> { if parser.parse_keyword(Keyword::BEGIN) { return Some(parser.parse_begin_exception_end()); diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 882803a5a..0971d9154 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2293,7 +2293,7 @@ impl<'a> Parser<'a> { // Snowflake permits a subquery to be passed as an argument without // an enclosing set of parens if it's the only argument. - if dialect_of!(self is SnowflakeDialect) && self.peek_sub_query() { + if self.dialect.supports_subquery_as_function_arg() && self.peek_sub_query() { let subquery = self.parse_query()?; self.expect_token(&Token::RParen)?; return Ok(Function { @@ -2683,8 +2683,7 @@ impl<'a> Parser<'a> { let syntax = if self.parse_keyword(Keyword::FROM) { ExtractSyntax::From - } else if self.consume_token(&Token::Comma) - && dialect_of!(self is SnowflakeDialect | GenericDialect) + } else if self.dialect.supports_extract_comma_syntax() && self.consume_token(&Token::Comma) { ExtractSyntax::Comma } else { @@ -6228,7 +6227,7 @@ impl<'a> Parser<'a> { None }; - let comment = if dialect_of!(self is SnowflakeDialect | GenericDialect) + let comment = if self.dialect.supports_create_view_comment_syntax() && self.parse_keyword(Keyword::COMMENT) { self.expect_token(&Token::Eq)?; @@ -11790,7 +11789,7 @@ impl<'a> Parser<'a> { Keyword::ENUM16 => Ok(DataType::Enum(self.parse_enum_values()?, Some(16))), Keyword::SET => Ok(DataType::Set(self.parse_string_values()?)), Keyword::ARRAY => { - if dialect_of!(self is SnowflakeDialect) { + if self.dialect.supports_array_typedef_without_element_type() { Ok(DataType::Array(ArrayElemTypeDef::None)) } else if dialect_of!(self is ClickHouseDialect) { Ok(self.parse_sub_type(|internal_type| { @@ -14989,7 +14988,7 @@ impl<'a> Parser<'a> { table_with_joins: Box::new(table_and_joins), alias, }) - } else if dialect_of!(self is SnowflakeDialect | GenericDialect) { + } else if self.dialect.supports_parens_around_table_factor() { // Dialect-specific behavior: Snowflake diverges from the // standard and from most of the other implementations by // allowing extra parentheses not only around a join (B), but @@ -15035,7 +15034,7 @@ impl<'a> Parser<'a> { // appearing alone in parentheses (e.g. `FROM (mytable)`) self.expected("joined table", self.peek_token()) } - } else if dialect_of!(self is SnowflakeDialect | DatabricksDialect | GenericDialect) + } else if self.dialect.supports_values_as_table_factor() && matches!( self.peek_tokens(), [