Repository: tobymao/sqlglot

Issue:
parsing failure for USE (snowflake)
`USE SECONDARY ROLES NONE`
dialect = Snowflake

Fails on `ParseError: Invalid expression / Unexpected token` for `NONE`

https://docs.snowflake.com/en/sql-reference/sql/use-secondary-roles

Discussion:

Solution patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index b823806ad3..1faadc7c4f 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -573,6 +573,16 @@ class Parser(parser.Parser):
             ),
         }
 
+        def _parse_use(self) -> exp.Use:
+            if self._match_text_seq("SECONDARY", "ROLES"):
+                this = self._match_texts(("ALL", "NONE")) and exp.var(self._prev.text.upper())
+                roles = None if this else self._parse_csv(lambda: self._parse_table(schema=False))
+                return self.expression(
+                    exp.Use, kind="SECONDARY ROLES", this=this, expressions=roles
+                )
+
+            return super()._parse_use()
+
         def _negate_range(
             self, this: t.Optional[exp.Expression] = None
         ) -> t.Optional[exp.Expression]:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index 4f02d0700d..6a4dd1101a 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -6822,7 +6822,7 @@ class Year(Func):
 
 
 class Use(Expression):
-    arg_types = {"this": True, "kind": False}
+    arg_types = {"this": False, "expressions": False, "kind": False}
 
 
 class Merge(DML):
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index 0059706835..1f0c63c5a0 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -3540,7 +3540,7 @@ def log_sql(self, expression: exp.Log) -> str:
     def use_sql(self, expression: exp.Use) -> str:
         kind = self.sql(expression, "kind")
         kind = f" {kind}" if kind else ""
-        this = self.sql(expression, "this")
+        this = self.sql(expression, "this") or self.expressions(expression, flat=True)
         this = f" {this}" if this else ""
         return f"USE{kind}{this}"
 
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 2c65ea6721..8326ae0cfc 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -829,11 +829,7 @@ class Parser(metaclass=_Parser):
         TokenType.UNCACHE: lambda self: self._parse_uncache(),
         TokenType.UNPIVOT: lambda self: self._parse_simplified_pivot(is_unpivot=True),
         TokenType.UPDATE: lambda self: self._parse_update(),
-        TokenType.USE: lambda self: self.expression(
-            exp.Use,
-            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
-            this=self._parse_table(schema=False),
-        ),
+        TokenType.USE: lambda self: self._parse_use(),
         TokenType.SEMICOLON: lambda self: exp.Semicolon(),
     }
 
@@ -2973,6 +2969,13 @@ def _parse_update(self) -> exp.Update:
             },
         )
 
+    def _parse_use(self) -> exp.Use:
+        return self.expression(
+            exp.Use,
+            kind=self._parse_var_from_options(self.USABLES, raise_unmatched=False),
+            this=self._parse_table(schema=False),
+        )
+
     def _parse_uncache(self) -> exp.Uncache:
         if not self._match(TokenType.TABLE):
             self.raise_error("Expecting TABLE after UNCACHE")


Test patch:
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
index 602a562ace..3c4c44ab70 100644
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -1427,18 +1427,10 @@ def test_ddl(self):
                     "CREATE TABLE t (id INT TAG (key1='value_1', key2='value_2'))",
                 )
 
+        self.validate_identity("USE SECONDARY ROLES ALL")
+        self.validate_identity("USE SECONDARY ROLES NONE")
+        self.validate_identity("USE SECONDARY ROLES a, b, c")
         self.validate_identity("CREATE SECURE VIEW table1 AS (SELECT a FROM table2)")
-        self.validate_identity(
-            """create external table et2(
-  col1 date as (parse_json(metadata$external_table_partition):COL1::date),
-  col2 varchar as (parse_json(metadata$external_table_partition):COL2::varchar),
-  col3 number as (parse_json(metadata$external_table_partition):COL3::number))
-  partition by (col1,col2,col3)
-  location=@s2/logs/
-  partition_type = user_specified
-  file_format = (type = parquet)""",
-            "CREATE EXTERNAL TABLE et2 (col1 DATE AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL1') AS DATE)), col2 VARCHAR AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL2') AS VARCHAR)), col3 DECIMAL(38, 0) AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL3') AS DECIMAL(38, 0)))) LOCATION @s2/logs/ PARTITION BY (col1, col2, col3) partition_type=user_specified file_format=(type = parquet)",
-        )
         self.validate_identity("CREATE OR REPLACE VIEW foo (uid) COPY GRANTS AS (SELECT 1)")
         self.validate_identity("CREATE TABLE geospatial_table (id INT, g GEOGRAPHY)")
         self.validate_identity("CREATE MATERIALIZED VIEW a COMMENT='...' AS SELECT 1 FROM x")
@@ -1504,6 +1496,17 @@ def test_ddl(self):
             "CREATE SEQUENCE seq1 WITH START=1 INCREMENT=1 ORDER",
             "CREATE SEQUENCE seq1 START=1 INCREMENT=1 ORDER",
         )
+        self.validate_identity(
+            """create external table et2(
+  col1 date as (parse_json(metadata$external_table_partition):COL1::date),
+  col2 varchar as (parse_json(metadata$external_table_partition):COL2::varchar),
+  col3 number as (parse_json(metadata$external_table_partition):COL3::number))
+  partition by (col1,col2,col3)
+  location=@s2/logs/
+  partition_type = user_specified
+  file_format = (type = parquet)""",
+            "CREATE EXTERNAL TABLE et2 (col1 DATE AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL1') AS DATE)), col2 VARCHAR AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL2') AS VARCHAR)), col3 DECIMAL(38, 0) AS (CAST(GET_PATH(PARSE_JSON(metadata$external_table_partition), 'COL3') AS DECIMAL(38, 0)))) LOCATION @s2/logs/ PARTITION BY (col1, col2, col3) partition_type=user_specified file_format=(type = parquet)",
+        )
 
         self.validate_all(
             "CREATE TABLE orders_clone CLONE orders",