Repository: tobymao/sqlglot

Issue:
rename_func doesn't support variable length functions
`rename_func` throws an error when used on functions with variable length arguments.

Example:

```Python
class Test(Dialect):
    class Generator(Generator):
        TRANSFORMS = {
            **Generator.TRANSFORMS,
            exp.Array: rename_func("LIST_VALUE"),
        }
```

Error:

```sh
✗ python -m sqlglot --read test "SELECT ARRAY(1,2,3)"
Traceback (most recent call last):
  File "/usr/local/Cellar/python@3.8/3.8.13/Frameworks/Python.framework/Versions/3.8/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/local/Cellar/python@3.8/3.8.13/Frameworks/Python.framework/Versions/3.8/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "sqlglot/sqlglot/__main__.py", line 54, in <module>
    sqls = sqlglot.transpile(
  File "sqlglot/sqlglot/__init__.py", line 93, in transpile
    return [
  File "sqlglot/sqlglot/__init__.py", line 94, in <listcomp>
    Dialect.get_or_raise(write)().generate(expression, **opts)
  File "sqlglot/sqlglot/dialects/dialect.py", line 138, in generate
    return self.generator(**opts).generate(expression)
  File "sqlglot/sqlglot/generator.py", line 177, in generate
    sql = self.sql(expression).strip()
  File "sqlglot/sqlglot/generator.py", line 259, in sql
    return getattr(self, exp_handler_name)(expression)
  File "sqlglot/sqlglot/generator.py", line 726, in select_sql
    expressions = self.expressions(expression)
  File "sqlglot/sqlglot/generator.py", line 1117, in expressions
    expressions = self.sep(sep).join(sql)
  File "sqlglot/sqlglot/generator.py", line 1111, in <genexpr>
    sql = (self.sql(e) for e in expressions)
  File "sqlglot/sqlglot/generator.py", line 250, in sql
    return transform(self, expression)
  File "sqlglot/sqlglot/dialects/dialect.py", line 175, in <lambda>
    return lambda self, expression: f"{name}({csv(*[self.sql(e) for e in expression.args.values()])})"
  File "sqlglot/sqlglot/dialects/dialect.py", line 175, in <listcomp>
    return lambda self, expression: f"{name}({csv(*[self.sql(e) for e in expression.args.values()])})"
  File "sqlglot/sqlglot/generator.py", line 255, in sql
    raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}")
ValueError: Expected an Expression. Received <class 'list'>: [(LITERAL this: 1, is_string: False), (LITERAL this: 2, is_string: False), (LITERAL this: 3, is_string: False)]
```

The DuckDB dialect uses a workaround (see https://github.com/tobymao/sqlglot/blob/main/sqlglot/dialects/duckdb.py#L111), but it should just work.



Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/dialect.py b/sqlglot/dialects/dialect.py
--- a/sqlglot/dialects/dialect.py
+++ b/sqlglot/dialects/dialect.py
@@ -171,7 +171,15 @@ def generator(self, **opts):
 
 
 def rename_func(name):
-    return lambda self, expression: f"{name}({csv(*[self.sql(e) for e in expression.args.values()])})"
+    def _rename(self, expression):
+        args = (
+            self.expressions(expression, flat=True)
+            if isinstance(expression, exp.Func) and expression.is_var_len_args
+            else csv(*[self.sql(e) for e in expression.args.values()])
+        )
+        return f"{name}({args})"
+
+    return _rename
 
 
 def approx_count_distinct_sql(self, expression):
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -108,7 +108,7 @@ class Generator(Generator):
         TRANSFORMS = {
             **Generator.TRANSFORMS,
             exp.ApproxDistinct: approx_count_distinct_sql,
-            exp.Array: lambda self, e: f"LIST_VALUE({self.expressions(e, flat=True)})",
+            exp.Array: rename_func("LIST_VALUE"),
             exp.ArraySize: rename_func("ARRAY_LENGTH"),
             exp.ArraySort: _array_sort_sql,
             exp.ArraySum: rename_func("LIST_SUM"),


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
@@ -66,6 +66,9 @@ def test_time(self):
     def test_duckdb(self):
         self.validate_all(
             "LIST_VALUE(0, 1, 2)",
+            read={
+                "spark": "ARRAY(0, 1, 2)",
+            },
             write={
                 "bigquery": "[0, 1, 2]",
                 "duckdb": "LIST_VALUE(0, 1, 2)",