Repository: tobymao/sqlglot

Issue:
aggregate filter compiles to invalid syntax for some dialects
**Fully reproducible code snippet**

```
In [18]: import sqlglot as sg

In [19]: sg.parse_one("select avg(x) filter (where true) from t", read="duckdb").sql("snowflake")
Out[19]: 'SELECT AVG(x) FILTER(WHERE TRUE) FROM t'
```

**Official Documentation**

Not sure where is documented in the Snowflake, but it doesn't work when I run queries in the UI

![image](https://github.com/tobymao/sqlglot/assets/417981/becb35e2-298b-4980-8030-c2d4a25d4866)


I suspect a translation into the dialect's `if`/`iif`/`iff` function would acceptable when a backend doesn't have `filter` syntax.



Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -385,6 +385,7 @@ class Generator(generator.Generator):
         JOIN_HINTS = False
         TABLE_HINTS = False
         QUERY_HINTS = False
+        AGGREGATE_FILTER_SUPPORTED = False
 
         TRANSFORMS = {
             **generator.Generator.TRANSFORMS,
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -191,6 +191,9 @@ class Generator:
     # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery)
     UNNEST_WITH_ORDINALITY = True
 
+    # Whether or not FILTER (WHERE cond) can be used for conditional aggregation
+    AGGREGATE_FILTER_SUPPORTED = True
+
     TYPE_MAPPING = {
         exp.DataType.Type.NCHAR: "CHAR",
         exp.DataType.Type.NVARCHAR: "VARCHAR",
@@ -959,9 +962,16 @@ def fetch_sql(self, expression: exp.Fetch) -> str:
         return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
 
     def filter_sql(self, expression: exp.Filter) -> str:
-        this = self.sql(expression, "this")
-        where = self.sql(expression, "expression").strip()
-        return f"{this} FILTER({where})"
+        if self.AGGREGATE_FILTER_SUPPORTED:
+            this = self.sql(expression, "this")
+            where = self.sql(expression, "expression").strip()
+            return f"{this} FILTER({where})"
+
+        agg = expression.this.copy()
+        agg_arg = agg.this
+        cond = expression.expression.this
+        agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy(), false=exp.null()))
+        return self.sql(agg)
 
     def hint_sql(self, expression: exp.Hint) -> str:
         if not self.QUERY_HINTS:


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -6,6 +6,20 @@ class TestDuckDB(Validator):
     dialect = "duckdb"
 
     def test_duckdb(self):
+        self.validate_all(
+            "WITH cte(x) AS (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) SELECT AVG(x) FILTER (WHERE x > 1) FROM cte",
+            write={
+                "duckdb": "WITH cte(x) AS (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) SELECT AVG(x) FILTER(WHERE x > 1) FROM cte",
+                "snowflake": "WITH cte(x) AS (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) SELECT AVG(IFF(x > 1, x, NULL)) FROM cte",
+            },
+        )
+        self.validate_all(
+            "SELECT AVG(x) FILTER (WHERE TRUE) FROM t",
+            write={
+                "duckdb": "SELECT AVG(x) FILTER(WHERE TRUE) FROM t",
+                "snowflake": "SELECT AVG(IFF(TRUE, x, NULL)) FROM t",
+            },
+        )
         self.validate_all(
             "SELECT UNNEST(ARRAY[1, 2, 3]), UNNEST(ARRAY[4, 5]), UNNEST(ARRAY[6])",
             write={