Repository: tobymao/sqlglot

Issue:
ClickHouse `quantiles` (note the `s`) fails to parse when an alias is present
I know I must be very annoying with all these ClickHouse parsing bug reports 😂 

`quantile` works:

```
In [17]: import sqlglot as sg

In [18]: sg.__version__
Out[18]: '10.4.3'

In [19]: sg.parse_one("select quantile(0.5)(a) AS x from t", read="clickhouse")
Out[19]:
(SELECT expressions:
  (ALIAS this:
    (QUANTILE this:
      (COLUMN this:
        (IDENTIFIER this: a, quoted: False)), quantile:
      (LITERAL this: 0.5, is_string: False)), alias:
    (IDENTIFIER this: x, quoted: False)), from:
  (FROM expressions:
    (TABLE this:
      (IDENTIFIER this: t, quoted: False))))
```

but `quantiles` does not:

```
In [20]: sg.parse_one("select quantiles(0.5)(a) AS x from t", read="clickhouse")
...
ParseError: Invalid expression / Unexpected token. Line 1, Col: 26.
  select quantiles(0.5)(a) AS x from t
```

*without* an alias, `quantiles` parses:

```
In [21]: sg.parse_one("select quantiles(0.5)(a) from t", read="clickhouse")
Out[21]:
(SELECT expressions:
  (ALIASES this:
    (ANONYMOUS this: quantiles, expressions:
      (LITERAL this: 0.5, is_string: False)), expressions:
    (IDENTIFIER this: a, quoted: False)), from:
  (FROM expressions:
    (TABLE this:
      (IDENTIFIER this: t, quoted: False))))
```




Discussion:
`quantiles` is the [array](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference/quantiles#quantiles) version of `quantile`.

Resolving patch:
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py
--- a/sqlglot/dialects/clickhouse.py
+++ b/sqlglot/dialects/clickhouse.py
@@ -11,13 +11,6 @@ def _lower_func(sql):
     return sql[:index].lower() + sql[index:]
 
 
-def _quantile_sql(self, expression: exp.Quantile) -> str:
-    if_suffix = "If" if len(expression.this) == 2 else ""
-    args = self.format_args(self.expressions(expression, "quantile"))
-    params = self.format_args(self.expressions(expression, "this"))
-    return f"quantile{if_suffix}({args})({params})"
-
-
 class ClickHouse(Dialect):
     normalize_functions = None
     null_ordering = "nulls_are_last"
@@ -44,8 +37,9 @@ class Parser(parser.Parser):
         FUNCTIONS = {
             **parser.Parser.FUNCTIONS,  # type: ignore
             "MAP": parse_var_map,
-            "QUANTILE": lambda args, params: exp.Quantile(this=params, quantile=args),
-            "QUANTILEIF": lambda args, params: exp.Quantile(this=params, quantile=args),
+            "QUANTILE": lambda params, args: exp.Quantile(this=args, quantile=params),
+            "QUANTILES": lambda params, args: exp.Quantiles(parameters=params, expressions=args),
+            "QUANTILEIF": lambda params, args: exp.QuantileIf(parameters=params, expressions=args),
         }
 
         JOIN_KINDS = {*parser.Parser.JOIN_KINDS, TokenType.ANY, TokenType.ASOF}  # type: ignore
@@ -85,7 +79,16 @@ class Generator(generator.Generator):
             exp.Final: lambda self, e: f"{self.sql(e, 'this')} FINAL",
             exp.Map: lambda self, e: _lower_func(var_map_sql(self, e)),
             exp.VarMap: lambda self, e: _lower_func(var_map_sql(self, e)),
-            exp.Quantile: _quantile_sql,
+            exp.Quantile: lambda self, e: f"quantile{self._param_args_sql(e, 'quantile', 'this')}",
+            exp.Quantiles: lambda self, e: f"quantiles{self._param_args_sql(e, 'parameters', 'expressions')}",
+            exp.QuantileIf: lambda self, e: f"quantileIf{self._param_args_sql(e, 'parameters', 'expressions')}",
         }
 
         EXPLICIT_UNION = True
+
+        def _param_args_sql(
+            self, expression: exp.Expression, params_name: str, args_name: str
+        ) -> str:
+            params = self.format_args(self.expressions(expression, params_name))
+            args = self.format_args(self.expressions(expression, args_name))
+            return f"({params})({args})"
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -2919,6 +2919,16 @@ class Quantile(AggFunc):
     arg_types = {"this": True, "quantile": True}
 
 
+# Clickhouse-specific:
+# https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference/quantiles/#quantiles
+class Quantiles(AggFunc):
+    arg_types = {"parameters": True, "expressions": True}
+
+
+class QuantileIf(AggFunc):
+    arg_types = {"parameters": True, "expressions": True}
+
+
 class ApproxQuantile(Quantile):
     arg_types = {"this": True, "quantile": True, "accuracy": False}
 


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
@@ -15,6 +15,7 @@ def test_clickhouse(self):
         self.validate_identity("SELECT * FROM foo ASOF JOIN bla")
         self.validate_identity("SELECT * FROM foo ANY JOIN bla")
         self.validate_identity("SELECT quantile(0.5)(a)")
+        self.validate_identity("SELECT quantiles(0.5)(a) AS x FROM t")
 
         self.validate_all(
             "SELECT fname, lname, age FROM person ORDER BY age DESC NULLS FIRST, fname ASC NULLS LAST, lname",