diff --git a/sqlglot/generators/clickhouse.py b/sqlglot/generators/clickhouse.py index d2b5381486..e95b375522 100644 --- a/sqlglot/generators/clickhouse.py +++ b/sqlglot/generators/clickhouse.py @@ -280,6 +280,7 @@ class ClickHouseGenerator(generator.Generator): exp.ArrayConcat: rename_func("arrayConcat"), exp.ArrayContains: rename_func("has"), exp.ArrayFilter: lambda self, e: self.func("arrayFilter", e.expression, e.this), + exp.Transform: lambda self, e: self.func("arrayMap", e.expression, e.this), exp.ArrayRemove: remove_from_array_using_filter, exp.ArrayReverse: rename_func("arrayReverse"), exp.ArraySlice: rename_func("arraySlice"), diff --git a/sqlglot/parsers/clickhouse.py b/sqlglot/parsers/clickhouse.py index 45bdf03af4..4e86252571 100644 --- a/sqlglot/parsers/clickhouse.py +++ b/sqlglot/parsers/clickhouse.py @@ -262,6 +262,12 @@ class ClickHouseParser(parser.Parser): "ARRAYMIN": exp.ArrayMin.from_arg_list, "ARRAYREVERSE": exp.ArrayReverse.from_arg_list, "ARRAYSLICE": exp.ArraySlice.from_arg_list, + # ClickHouse higher-order array functions: the lambda comes first, the array second. + # This is the opposite of exp.ArrayFilter(this=array, expression=lambda) convention. + "ARRAYFILTER": lambda args: exp.ArrayFilter( + this=seq_get(args, 1), expression=seq_get(args, 0) + ), + "ARRAYMAP": lambda args: exp.Transform(this=seq_get(args, 1), expression=seq_get(args, 0)), "CURRENTDATABASE": exp.CurrentDatabase.from_arg_list, "CURRENTSCHEMAS": exp.CurrentSchemas.from_arg_list, "COUNTIF": _build_count_if, diff --git a/tests/dialects/test_clickhouse.py b/tests/dialects/test_clickhouse.py index f81ff63904..d05738dac7 100644 --- a/tests/dialects/test_clickhouse.py +++ b/tests/dialects/test_clickhouse.py @@ -1768,6 +1768,28 @@ def test_functions(self): self.validate_identity( "SELECT TRANSFORM(foo, [1, 2], ['first', 'second'], 'default') FROM table" ) + self.validate_all( + "SELECT arrayMap(x -> x + 1, arr) FROM t", + read={ + "duckdb": "SELECT LIST_TRANSFORM(arr, x -> x + 1) FROM t", + "spark": "SELECT TRANSFORM(arr, x -> x + 1) FROM t", + }, + write={ + "clickhouse": "SELECT arrayMap(x -> x + 1, arr) FROM t", + "duckdb": "SELECT LIST_TRANSFORM(arr, x -> x + 1) FROM t", + "spark": "SELECT TRANSFORM(arr, x -> x + 1) FROM t", + }, + ) + self.validate_all( + "SELECT arrayFilter(x -> x > 0, arr) FROM t", + read={ + "duckdb": "SELECT LIST_FILTER(arr, x -> x > 0) FROM t", + }, + write={ + "clickhouse": "SELECT arrayFilter(x -> x > 0, arr) FROM t", + "duckdb": "SELECT LIST_FILTER(arr, x -> x > 0) FROM t", + }, + ) def test_array_offset(self): with self.assertLogs(helper_logger) as cm: