Repository: tobymao/sqlglot

Issue:
Valid ClickHouse expression fails to parse
**Before you file an issue**
- Make sure you specify the "read" dialect eg. parse_one(sql, read="spark"): **See the code snippet below**
- Check if the issue still exists on main: **Tried against `main`, still exists**

**Fully reproducible code snippet**

Successful clickhouse fiddle query:
https://fiddle.clickhouse.com/604acdad-bbe4-4226-97ae-5e825aa79db2

Failing sqlglot snippet:

```
In [3]: import sqlglot as sg

In [4]: sg.__version__
Out[4]: '14.0.0'

In [5]: sg.parse_one("SELECT CAST(tuple(1 as `a`, 2 as `b`, 3.0 as `c`).2 AS Nullable(
   ...: String))", read="clickhouse")
```

**Official Documentation**

I'm not sure what "official SQL documentation" means 😅.



Discussion:
This used to work before 14.0.0, if that helps.
This isn't a blocker for us in ibis, it turned out that we don't actually need to generate the `as` bits, just wanted to flag this as a bug.
Hey @cpcloud, I'm already looking into this. We saw the ibis CI failure 😅 .

Thanks for the report!

Resolving patch:
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py
--- a/sqlglot/dialects/clickhouse.py
+++ b/sqlglot/dialects/clickhouse.py
@@ -64,6 +64,8 @@ class Parser(parser.Parser):
             "UNIQ": exp.ApproxDistinct.from_arg_list,
         }
 
+        FUNCTIONS_WITH_ALIASED_ARGS = {*parser.Parser.FUNCTIONS_WITH_ALIASED_ARGS, "TUPLE"}
+
         FUNCTION_PARSERS = {
             **parser.Parser.FUNCTION_PARSERS,
             "QUANTILE": lambda self: self._parse_quantile(),
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -678,6 +678,8 @@ class Parser(metaclass=_Parser):
         ),
     }
 
+    FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
+
     FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
         "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
         "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
@@ -691,7 +693,6 @@ class Parser(metaclass=_Parser):
         "SAFE_CAST": lambda self: self._parse_cast(False),
         "STRING_AGG": lambda self: self._parse_string_agg(),
         "SUBSTRING": lambda self: self._parse_substring(),
-        "STRUCT": lambda self: self._parse_struct(),
         "TRIM": lambda self: self._parse_trim(),
         "TRY_CAST": lambda self: self._parse_cast(False),
         "TRY_CONVERT": lambda self: self._parse_convert(False),
@@ -3119,7 +3120,9 @@ def _parse_function(
                 functions = self.FUNCTIONS
 
             function = functions.get(upper)
-            args = self._parse_csv(self._parse_lambda)
+
+            alias = upper in self.FUNCTIONS_WITH_ALIASED_ARGS
+            args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
 
             if function and not anonymous:
                 this = function(args)
@@ -3790,9 +3793,6 @@ def _parse_substring(self) -> exp.Expression:
 
         return this
 
-    def _parse_struct(self) -> exp.Struct:
-        return exp.Struct.from_arg_list(self._parse_csv(lambda: self._parse_lambda(alias=True)))
-
     def _parse_trim(self) -> exp.Expression:
         # https://www.w3resource.com/sql/character-functions/trim.php
         # https://docs.oracle.com/javadb/10.8.3.0/ref/rreftrimfunc.html


Test patch:
diff --git a/tests/dialects/test_clickhouse.py b/tests/dialects/test_clickhouse.py
--- a/tests/dialects/test_clickhouse.py
+++ b/tests/dialects/test_clickhouse.py
@@ -33,6 +33,9 @@ def test_clickhouse(self):
         self.validate_identity("position(haystack, needle)")
         self.validate_identity("position(haystack, needle, position)")
         self.validate_identity("CAST(x AS DATETIME)")
+        self.validate_identity(
+            'SELECT CAST(tuple(1 AS "a", 2 AS "b", 3.0 AS "c").2 AS Nullable(TEXT))'
+        )
         self.validate_identity(
             "CREATE TABLE test (id UInt8) ENGINE=AggregatingMergeTree() ORDER BY tuple()"
         )