diff --git a/sqlglot/generator.py b/sqlglot/generator.py index 2ab295e471..8f3e52ae95 100644 --- a/sqlglot/generator.py +++ b/sqlglot/generator.py @@ -5668,7 +5668,15 @@ def _add_arrayagg_null_filter( def arrayagg_sql(self, expression: exp.ArrayAgg) -> str: array_agg = self.function_fallback_sql(expression) - return self._add_arrayagg_null_filter(array_agg, expression, expression.this) + # The argument inside ARRAY_AGG may be wrapped in Order and/or Limit + # nodes. Unwrap to get the bare column expression so the FILTER + # clause is just "WHERE x IS NOT NULL". + column_expr = expression.this + if isinstance(column_expr, exp.Limit): + column_expr = column_expr.this + if isinstance(column_expr, exp.Order): + column_expr = column_expr.this + return self._add_arrayagg_null_filter(array_agg, expression, column_expr) def slice_sql(self, expression: exp.Slice) -> str: step = self.sql(expression, "step") diff --git a/sqlglot/generators/duckdb.py b/sqlglot/generators/duckdb.py index ce31a22c12..4532caea3c 100644 --- a/sqlglot/generators/duckdb.py +++ b/sqlglot/generators/duckdb.py @@ -3953,6 +3953,11 @@ def unnest_sql(self, expression: exp.Unnest) -> str: return super().unnest_sql(expression) + def arrayagg_sql(self, expression: exp.ArrayAgg) -> str: + if isinstance(expression.this, exp.Limit): + self.unsupported("LIMIT inside ARRAY_AGG is not supported in DuckDB") + return super().arrayagg_sql(expression) + def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: this = expression.this @@ -3961,6 +3966,14 @@ def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: # window functions that accept it e.g. FIRST_VALUE(... IGNORE NULLS) OVER (...) return super().ignorenulls_sql(expression) + # For ARRAY_AGG(expr IGNORE NULLS ...), convert IGNORE NULLS to a + # FILTER(WHERE expr IS NOT NULL) clause by setting nulls_excluded on + # the ArrayAgg. The existing _add_arrayagg_null_filter method will + # emit the FILTER clause during arrayagg_sql / withingroup_sql. + if isinstance(this, exp.ArrayAgg): + this.set("nulls_excluded", True) + return self.sql(this) + if isinstance(this, exp.First): this = exp.AnyValue(this=this.this) diff --git a/tests/dialects/test_bigquery.py b/tests/dialects/test_bigquery.py index 9e7834b6a7..f0e3151773 100644 --- a/tests/dialects/test_bigquery.py +++ b/tests/dialects/test_bigquery.py @@ -737,18 +737,10 @@ def test_bigquery(self): "SELECT ARRAY_AGG(DISTINCT x IGNORE NULLS ORDER BY a, b DESC LIMIT 10) AS x", write={ "bigquery": "SELECT ARRAY_AGG(DISTINCT x IGNORE NULLS ORDER BY a, b DESC LIMIT 10) AS x", - "duckdb": "SELECT ARRAY_AGG(DISTINCT x ORDER BY a NULLS FIRST, b DESC LIMIT 10) AS x", + "duckdb": UnsupportedError, "spark": "SELECT COLLECT_LIST(DISTINCT x ORDER BY a, b DESC LIMIT 10) IGNORE NULLS AS x", }, ) - self.validate_all( - "SELECT ARRAY_AGG(DISTINCT x IGNORE NULLS ORDER BY a, b DESC LIMIT 1, 10) AS x", - write={ - "bigquery": "SELECT ARRAY_AGG(DISTINCT x IGNORE NULLS ORDER BY a, b DESC LIMIT 1, 10) AS x", - "duckdb": "SELECT ARRAY_AGG(DISTINCT x ORDER BY a NULLS FIRST, b DESC LIMIT 1, 10) AS x", - "spark": "SELECT COLLECT_LIST(DISTINCT x ORDER BY a, b DESC LIMIT 1, 10) IGNORE NULLS AS x", - }, - ) self.validate_all( "SELECT * FROM Produce UNPIVOT((first_half_sales, second_half_sales) FOR semesters IN ((Q1, Q2) AS 'semester_1', (Q3, Q4) AS 'semester_2'))", read={