From 31f4dc7b99a1db82f66f518af50a219f20117ad5 Mon Sep 17 00:00:00 2001 From: osipovartem Date: Wed, 9 Sep 2026 01:26:18 +0300 Subject: [PATCH] Snowflake: parse structured ARRAY types --- src/dialect/clickhouse.rs | 4 ++++ src/dialect/mod.rs | 10 ++++++++++ src/dialect/snowflake.rs | 5 +++++ src/parser/mod.rs | 8 +++++--- tests/sqlparser_snowflake.rs | 12 ++++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/dialect/clickhouse.rs b/src/dialect/clickhouse.rs index c81d953d1b..1a1f168915 100644 --- a/src/dialect/clickhouse.rs +++ b/src/dialect/clickhouse.rs @@ -79,6 +79,10 @@ impl Dialect for ClickHouseDialect { true } + fn supports_array_typedef_with_parentheses(&self) -> bool { + true + } + // ClickHouse uses this for some FORMAT expressions in `INSERT` context, e.g. when inserting // with FORMAT JSONEachRow a raw JSON key-value expression is valid and expected. // diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index ff83a4da61..1b74f364dd 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1083,6 +1083,16 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports the `ARRAY(element_type)` syntax. + /// + /// Example: + /// ```sql + /// CREATE TABLE t (a ARRAY(VARCHAR)); + /// ``` + fn supports_array_typedef_with_parentheses(&self) -> bool { + false + } + /// Returns true if this dialect supports extra parentheses around /// lone table names or derived tables in the `FROM` clause. /// diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index 0bedb12a55..4e07b01aa2 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -240,6 +240,11 @@ impl Dialect for SnowflakeDialect { true } + /// See [doc](https://docs.snowflake.com/en/sql-reference/data-types-structured#label-structured-types-array) + fn supports_array_typedef_with_parentheses(&self) -> bool { + true + } + /// See [doc](https://docs.snowflake.com/en/sql-reference/constructs/from) fn supports_parens_around_table_factor(&self) -> bool { true diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5edc437145..6013858b05 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -13101,12 +13101,14 @@ 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 self.dialect.supports_array_typedef_without_element_type() { - Ok(DataType::Array(ArrayElemTypeDef::None)) - } else if dialect_of!(self is ClickHouseDialect) { + if self.dialect.supports_array_typedef_with_parentheses() + && self.peek_token_ref().token == Token::LParen + { Ok(self.parse_sub_type(|internal_type| { DataType::Array(ArrayElemTypeDef::Parenthesis(internal_type)) })?) + } else if self.dialect.supports_array_typedef_without_element_type() { + Ok(DataType::Array(ArrayElemTypeDef::None)) } else { self.expect_token(&Token::Lt)?; let (inside_type, _trailing_bracket) = self.parse_data_type_helper()?; diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 059560dcc9..df3f09d9e4 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -4912,3 +4912,15 @@ fn test_select_dollar_column_from_stage() { // With table function args, without alias snowflake().verified_stmt("SELECT $1, $2 FROM @mystage1(file_format => 'myformat')"); } + +#[test] +fn test_structured_array_type() { + snowflake().one_statement_parses_to( + "CREATE TABLE t (a ARRAY(VARCHAR))", + "CREATE TABLE t (a Array(VARCHAR))", + ); + snowflake().one_statement_parses_to( + "SELECT CAST(a AS ARRAY(NUMBER(10, 2))) FROM t", + "SELECT CAST(a AS Array(NUMBER(10, 2))) FROM t", + ); +}