Repository: tobymao/sqlglot

Issue:
wrong transformation from bigquery struct to presto row
**Before you file an issue**
- Make sure you specify the "read" dialect eg. parse_one(sql, read="spark")
- Check if the issue still exists on main 

**Fully reproducible code snippet**
```
>>> sqlglot.transpile("""select struct(true as sign, 1 as value)
... """, read="bigquery", write="presto")[0]
'SELECT ROW(TRUE AS sign, 1 AS value)'
```
and
```
trino> SELECT ROW(TRUE AS sign, 1 AS value)
    -> ;
Query 20240129_170954_00005_56ath failed: line 1:17: mismatched input 'AS'. Expecting: '%', ')', '*', '+', ',', '-', '.', '/', 'AND', 'AT', 'OR', 'ORDER', '[', '||', <predicate>
SELECT ROW(TRUE AS sign, 1 AS value)
```


**Official Documentation**
Please include links to official SQL documentation related to your issue.


Discussion:
georgesittas: Nice!

Patch:
diff --git a/sqlglot/dialects/bigquery.py b/sqlglot/dialects/bigquery.py
index fed488d9..36565d05 100644
--- a/sqlglot/dialects/bigquery.py
+++ b/sqlglot/dialects/bigquery.py
@@ -42,7 +42,10 @@ def _derived_table_values_to_unnest(self: BigQuery.Generator, expression: exp.Va
     alias = expression.args.get("alias")
     for tup in expression.find_all(exp.Tuple):
         field_aliases = alias.columns if alias else (f"_c{i}" for i in range(len(tup.expressions)))
-        expressions = [exp.alias_(fld, name) for fld, name in zip(tup.expressions, field_aliases)]
+        expressions = [
+            exp.PropertyEQ(this=exp.to_identifier(name), expression=fld)
+            for name, fld in zip(field_aliases, tup.expressions)
+        ]
         structs.append(exp.Struct(expressions=expressions))
 
     return self.unnest_sql(exp.Unnest(expressions=[exp.array(*structs, copy=False)]))
@@ -799,18 +802,6 @@ class BigQuery(Dialect):
             this = expression.this if isinstance(expression.this, exp.TsOrDsToDate) else expression
             return self.func("FORMAT_DATE", self.format_time(expression), this.this)
 
-        def struct_sql(self, expression: exp.Struct) -> str:
-            args = []
-            for expr in expression.expressions:
-                if isinstance(expr, self.KEY_VALUE_DEFINITIONS):
-                    arg = f"{self.sql(expr, 'expression')} AS {expr.this.name}"
-                else:
-                    arg = self.sql(expr)
-
-                args.append(arg)
-
-            return self.func("STRUCT", *args)
-
         def eq_sql(self, expression: exp.EQ) -> str:
             # Operands of = cannot be NULL in BigQuery
             if isinstance(expression.left, exp.Null) or isinstance(expression.right, exp.Null):
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py
index 05d6a035..910d129f 100644
--- a/sqlglot/dialects/clickhouse.py
+++ b/sqlglot/dialects/clickhouse.py
@@ -129,6 +129,7 @@ class ClickHouse(Dialect):
             "MAP": parser.build_var_map,
             "MATCH": exp.RegexpLike.from_arg_list,
             "RANDCANONICAL": exp.Rand.from_arg_list,
+            "TUPLE": exp.Struct.from_arg_list,
             "UNIQ": exp.ApproxDistinct.from_arg_list,
             "XOR": lambda args: exp.Xor(expressions=args),
         }
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
index 849239f6..d756dcc7 100644
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -110,13 +110,13 @@ def _build_make_timestamp(args: t.List) -> exp.Expression:
 
 def _struct_sql(self: DuckDB.Generator, expression: exp.Struct) -> str:
     args: t.List[str] = []
-    for expr in expression.expressions:
-        if isinstance(expr, exp.Alias):
-            key = expr.alias
-            value = expr.this
-        else:
-            key = expr.name or expr.this.name
+    for i, expr in enumerate(expression.expressions):
+        if isinstance(expr, exp.PropertyEQ):
+            key = expr.name
             value = expr.expression
+        else:
+            key = f"_{i}"
+            value = expr
 
         args.append(f"{self.sql(exp.Literal.string(key))}: {self.sql(value)}")
 
@@ -163,13 +163,6 @@ def _rename_unless_within_group(
     )
 
 
-def _build_struct_pack(args: t.List) -> exp.Struct:
-    args_with_columns_as_identifiers = [
-        exp.PropertyEQ(this=arg.this.this, expression=arg.expression) for arg in args
-    ]
-    return exp.Struct.from_arg_list(args_with_columns_as_identifiers)
-
-
 class DuckDB(Dialect):
     NULL_ORDERING = "nulls_are_last"
     SUPPORTS_USER_DEFINED_TYPES = False
@@ -228,6 +221,8 @@ class DuckDB(Dialect):
             TokenType.TILDA: exp.RegexpLike,
         }
 
+        FUNCTIONS_WITH_ALIASED_ARGS = {*parser.Parser.FUNCTIONS_WITH_ALIASED_ARGS, "STRUCT_PACK"}
+
         FUNCTIONS = {
             **parser.Parser.FUNCTIONS,
             "ARRAY_HAS": exp.ArrayContains.from_arg_list,
@@ -276,7 +271,7 @@ class DuckDB(Dialect):
             "STRING_SPLIT_REGEX": exp.RegexpSplit.from_arg_list,
             "STRING_TO_ARRAY": exp.Split.from_arg_list,
             "STRPTIME": build_formatted_time(exp.StrToTime, "duckdb"),
-            "STRUCT_PACK": _build_struct_pack,
+            "STRUCT_PACK": exp.Struct.from_arg_list,
             "STR_SPLIT": exp.Split.from_arg_list,
             "STR_SPLIT_REGEX": exp.RegexpSplit.from_arg_list,
             "TO_TIMESTAMP": exp.UnixToTime.from_arg_list,
diff --git a/sqlglot/dialects/hive.py b/sqlglot/dialects/hive.py
index 43211dc5..50c41f81 100644
--- a/sqlglot/dialects/hive.py
+++ b/sqlglot/dialects/hive.py
@@ -631,3 +631,15 @@ class Hive(Dialect):
         def version_sql(self, expression: exp.Version) -> str:
             sql = super().version_sql(expression)
             return sql.replace("FOR ", "", 1)
+
+        def struct_sql(self, expression: exp.Struct) -> str:
+            values = []
+
+            for i, e in enumerate(expression.expressions):
+                if isinstance(e, exp.PropertyEQ):
+                    self.unsupported("Hive does not support named structs.")
+                    values.append(e.expression)
+                else:
+                    values.append(e)
+
+            return self.func("STRUCT", *values)
diff --git a/sqlglot/dialects/presto.py b/sqlglot/dialects/presto.py
index 84295476..3649bd2f 100644
--- a/sqlglot/dialects/presto.py
+++ b/sqlglot/dialects/presto.py
@@ -453,11 +453,32 @@ class Presto(Dialect):
             return super().bracket_sql(expression)
 
         def struct_sql(self, expression: exp.Struct) -> str:
-            if any(isinstance(arg, self.KEY_VALUE_DEFINITIONS) for arg in expression.expressions):
-                self.unsupported("Struct with key-value definitions is unsupported.")
-                return self.function_fallback_sql(expression)
+            from sqlglot.optimizer.annotate_types import annotate_types
+
+            expression = annotate_types(expression)
+            values: t.List[str] = []
+            schema: t.List[str] = []
+            unknown_type = False
+
+            for e in expression.expressions:
+                if isinstance(e, exp.PropertyEQ):
+                    if e.type and e.type.is_type(exp.DataType.Type.UNKNOWN):
+                        unknown_type = True
+                    else:
+                        schema.append(f"{self.sql(e, 'this')} {self.sql(e.type)}")
+                    values.append(self.sql(e, "expression"))
+                else:
+                    values.append(self.sql(e))
+
+            size = len(expression.expressions)
 
-            return rename_func("ROW")(self, expression)
+            if not size or len(schema) != size:
+                if unknown_type:
+                    self.unsupported(
+                        "Cannot convert untyped key-value definitions (try annotate_types)."
+                    )
+                return self.func("ROW", *values)
+            return f"CAST(ROW({', '.join(values)}) AS ROW({', '.join(schema)}))"
 
         def interval_sql(self, expression: exp.Interval) -> str:
             unit = self.sql(expression, "unit")
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index cb61cabd..e34cbcb7 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -21,7 +21,7 @@ from sqlglot.dialects.dialect import (
     var_map_sql,
 )
 from sqlglot.expressions import Literal
-from sqlglot.helper import is_int, seq_get
+from sqlglot.helper import flatten, is_int, seq_get
 from sqlglot.tokens import TokenType
 
 if t.TYPE_CHECKING:
@@ -66,7 +66,7 @@ def _build_object_construct(args: t.List) -> t.Union[exp.StarMap, exp.Struct]:
 
     return exp.Struct(
         expressions=[
-            t.cast(exp.Condition, k).eq(v) for k, v in zip(expression.keys, expression.values)
+            exp.PropertyEQ(this=k, expression=v) for k, v in zip(expression.keys, expression.values)
         ]
     )
 
@@ -768,10 +768,6 @@ class Snowflake(Dialect):
                 "POSITION", e.args.get("substr"), e.this, e.args.get("position")
             ),
             exp.StrToTime: lambda self, e: self.func("TO_TIMESTAMP", e.this, self.format_time(e)),
-            exp.Struct: lambda self, e: self.func(
-                "OBJECT_CONSTRUCT",
-                *(arg for expression in e.expressions for arg in expression.flatten()),
-            ),
             exp.Stuff: rename_func("INSERT"),
             exp.TimestampDiff: lambda self, e: self.func(
                 "TIMESTAMPDIFF", e.unit, e.expression, e.this
@@ -947,3 +943,19 @@ class Snowflake(Dialect):
 
         def cluster_sql(self, expression: exp.Cluster) -> str:
             return f"CLUSTER BY ({self.expressions(expression, flat=True)})"
+
+        def struct_sql(self, expression: exp.Struct) -> str:
+            keys = []
+            values = []
+
+            for i, e in enumerate(expression.expressions):
+                if isinstance(e, exp.PropertyEQ):
+                    keys.append(
+                        exp.Literal.string(e.name) if isinstance(e.this, exp.Identifier) else e.this
+                    )
+                    values.append(e.expression)
+                else:
+                    keys.append(exp.Literal.string(f"_{i}"))
+                    values.append(e)
+
+            return self.func("OBJECT_CONSTRUCT", *flatten(zip(keys, values)))
diff --git a/sqlglot/dialects/spark2.py b/sqlglot/dialects/spark2.py
index 60cf8e13..63eae6ee 100644
--- a/sqlglot/dialects/spark2.py
+++ b/sqlglot/dialects/spark2.py
@@ -263,14 +263,9 @@ class Spark2(Hive):
         CREATE_FUNCTION_RETURN_AS = False
 
         def struct_sql(self, expression: exp.Struct) -> str:
-            args = []
-            for arg in expression.expressions:
-                if isinstance(arg, self.KEY_VALUE_DEFINITIONS):
-                    args.append(exp.alias_(arg.expression, arg.this.name))
-                else:
-                    args.append(arg)
-
-            return self.func("STRUCT", *args)
+            from sqlglot.generator import Generator
+
+            return Generator.struct_sql(self, expression)
 
         def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str:
             if is_parse_json(expression.this):
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index e1b0c191..01a8160a 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -462,8 +462,6 @@ class Generator(metaclass=_Generator):
     # Expressions that need to have all CTEs under them bubbled up to them
     EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set()
 
-    KEY_VALUE_DEFINITIONS = (exp.EQ, exp.PropertyEQ, exp.Slice)
-
     SENTINEL_LINE_BREAK = "__SQLGLOT__LB__"
 
     __slots__ = (
@@ -2140,9 +2138,9 @@ class Generator(metaclass=_Generator):
                             self.sql(
                                 exp.Struct(
                                     expressions=[
-                                        exp.column(e.output_name).eq(
-                                            e.this if isinstance(e, exp.Alias) else e
-                                        )
+                                        exp.PropertyEQ(this=e.args.get("alias"), expression=e.this)
+                                        if isinstance(e, exp.Alias)
+                                        else e
                                         for e in expression.expressions
                                     ]
                                 )
@@ -3458,3 +3456,14 @@ class Generator(metaclass=_Generator):
     def generateseries_sql(self, expression: exp.GenerateSeries) -> str:
         expression.set("is_end_exclusive", None)
         return self.function_fallback_sql(expression)
+
+    def struct_sql(self, expression: exp.Struct) -> str:
+        expression.set(
+            "expressions",
+            [
+                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
+                for e in expression.expressions
+            ],
+        )
+
+        return self.function_fallback_sql(expression)
diff --git a/sqlglot/optimizer/annotate_types.py b/sqlglot/optimizer/annotate_types.py
index 3ce0f8fc..81b1ee65 100644
--- a/sqlglot/optimizer/annotate_types.py
+++ b/sqlglot/optimizer/annotate_types.py
@@ -275,6 +275,7 @@ class TypeAnnotator(metaclass=_TypeAnnotator):
         exp.Min: lambda self, e: self._annotate_by_args(e, "this", "expressions"),
         exp.Null: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.NULL),
         exp.Nullif: lambda self, e: self._annotate_by_args(e, "this", "expression"),
+        exp.PropertyEQ: lambda self, e: self._annotate_by_args(e, "expression"),
         exp.Slice: lambda self, e: self._annotate_with_type(e, exp.DataType.Type.UNKNOWN),
         exp.Struct: lambda self, e: self._annotate_by_args(e, "expressions", struct=True),
         exp.Sum: lambda self, e: self._annotate_by_args(e, "this", "expressions", promote=True),
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index df440906..7200940d 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -875,6 +875,8 @@ class Parser(metaclass=_Parser):
 
     FUNCTIONS_WITH_ALIASED_ARGS = {"STRUCT"}
 
+    KEY_VALUE_DEFINITIONS = (exp.Alias, exp.EQ, exp.PropertyEQ, exp.Slice)
+
     FUNCTION_PARSERS = {
         "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
         "CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
@@ -4112,6 +4114,9 @@ class Parser(metaclass=_Parser):
             alias = upper in self.FUNCTIONS_WITH_ALIASED_ARGS
             args = self._parse_csv(lambda: self._parse_lambda(alias=alias))
 
+            if alias:
+                args = self._kv_to_prop_eq(args)
+
             if function and not anonymous:
                 if "dialect" in function.__code__.co_varnames:
                     func = function(args, dialect=self.dialect)
@@ -4132,6 +4137,26 @@ class Parser(metaclass=_Parser):
         self._match_r_paren(this)
         return self._parse_window(this)
 
+    def _kv_to_prop_eq(self, expressions: t.List[exp.Expression]) -> t.List[exp.Expression]:
+        transformed = []
+
+        for e in expressions:
+            if isinstance(e, self.KEY_VALUE_DEFINITIONS):
+                if isinstance(e, exp.Alias):
+                    e = self.expression(exp.PropertyEQ, this=e.args.get("alias"), expression=e.this)
+
+                if not isinstance(e, exp.PropertyEQ):
+                    e = self.expression(
+                        exp.PropertyEQ, this=exp.to_identifier(e.name), expression=e.expression
+                    )
+
+                if isinstance(e.this, exp.Column):
+                    e.this.replace(e.this.this)
+
+            transformed.append(e)
+
+        return transformed
+
     def _parse_function_parameter(self) -> t.Optional[exp.Expression]:
         return self._parse_column_def(self._parse_id_var())
 
@@ -4544,7 +4569,7 @@ class Parser(metaclass=_Parser):
 
         # https://duckdb.org/docs/sql/data_types/struct.html#creating-structs
         if bracket_kind == TokenType.L_BRACE:
-            this = self.expression(exp.Struct, expressions=expressions)
+            this = self.expression(exp.Struct, expressions=self._kv_to_prop_eq(expressions))
         elif not this or this.name.upper() == "ARRAY":
             this = self.expression(exp.Array, expressions=expressions)
         else:
diff --git a/sqlglot/transforms.py b/sqlglot/transforms.py
index f11c044d..04c1f7b1 100644
--- a/sqlglot/transforms.py
+++ b/sqlglot/transforms.py
@@ -560,6 +560,22 @@ def move_partitioned_by_to_schema_columns(expression: exp.Expression) -> exp.Exp
     return expression
 
 
+def struct_kv_to_alias(expression: exp.Expression) -> exp.Expression:
+    """
+    Convert struct arguments to aliases: STRUCT(1 AS y) .
+    """
+    if isinstance(expression, exp.Struct):
+        expression.set(
+            "expressions",
+            [
+                exp.alias_(e.expression, e.this) if isinstance(e, exp.PropertyEQ) else e
+                for e in expression.expressions
+            ],
+        )
+
+    return expression
+
+
 def preprocess(
     transforms: t.List[t.Callable[[exp.Expression], exp.Expression]],
 ) -> t.Callable[[Generator, exp.Expression], str]:


Test patch:
diff --git a/tests/dialects/test_bigquery.py b/tests/dialects/test_bigquery.py
index 49062ae6..0c13a830 100644
--- a/tests/dialects/test_bigquery.py
+++ b/tests/dialects/test_bigquery.py
@@ -19,6 +19,27 @@ class TestBigQuery(Validator):
     maxDiff = None
 
     def test_bigquery(self):
+        self.validate_all(
+            "SELECT STRUCT(1, 2, 3), STRUCT(), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1 as a, 'abc' AS b), STRUCT(str_col AS abc)",
+            write={
+                "bigquery": "SELECT STRUCT(1, 2, 3), STRUCT(), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1 AS a, 'abc' AS b), STRUCT(str_col AS abc)",
+                "duckdb": "SELECT {'_0': 1, '_1': 2, '_2': 3}, {}, {'_0': 'abc'}, {'_0': 1, '_1': t.str_col}, {'a': 1, 'b': 'abc'}, {'abc': str_col}",
+                "hive": "SELECT STRUCT(1, 2, 3), STRUCT(), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1, 'abc'), STRUCT(str_col)",
+                "spark2": "SELECT STRUCT(1, 2, 3), STRUCT(), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1 AS a, 'abc' AS b), STRUCT(str_col AS abc)",
+                "spark": "SELECT STRUCT(1, 2, 3), STRUCT(), STRUCT('abc'), STRUCT(1, t.str_col), STRUCT(1 AS a, 'abc' AS b), STRUCT(str_col AS abc)",
+                "snowflake": "SELECT OBJECT_CONSTRUCT('_0', 1, '_1', 2, '_2', 3), OBJECT_CONSTRUCT(), OBJECT_CONSTRUCT('_0', 'abc'), OBJECT_CONSTRUCT('_0', 1, '_1', t.str_col), OBJECT_CONSTRUCT('a', 1, 'b', 'abc'), OBJECT_CONSTRUCT('abc', str_col)",
+                # fallback to unnamed without type inference
+                "trino": "SELECT ROW(1, 2, 3), ROW(), ROW('abc'), ROW(1, t.str_col), CAST(ROW(1, 'abc') AS ROW(a INTEGER, b VARCHAR)), ROW(str_col)",
+            },
+        )
+        self.validate_all(
+            "PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E6S%z', x)",
+            write={
+                "bigquery": "PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E6S%z', x)",
+                "duckdb": "STRPTIME(x, '%Y-%m-%dT%H:%M:%S.%f%z')",
+            },
+        )
+
         self.assertEqual(exp.to_table("`x.y.z`", dialect="bigquery").sql(), '"x"."y"."z"')
         self.assertEqual(exp.to_table("`x.y.z`", dialect="bigquery").sql("bigquery"), "`x.y.z`")
         self.assertEqual(exp.to_table("`x`.`y`", dialect="bigquery").sql("bigquery"), "`x`.`y`")
@@ -615,11 +636,11 @@ class TestBigQuery(Validator):
             read={"spark": "select posexplode_outer([])"},
         )
         self.validate_all(
-            "SELECT AS STRUCT ARRAY(SELECT AS STRUCT b FROM x) AS y FROM z",
+            "SELECT AS STRUCT ARRAY(SELECT AS STRUCT 1 AS b FROM x) AS y FROM z",
             write={
-                "": "SELECT AS STRUCT ARRAY(SELECT AS STRUCT b FROM x) AS y FROM z",
-                "bigquery": "SELECT AS STRUCT ARRAY(SELECT AS STRUCT b FROM x) AS y FROM z",
-                "duckdb": "SELECT {'y': ARRAY(SELECT {'b': b} FROM x)} FROM z",
+                "": "SELECT AS STRUCT ARRAY(SELECT AS STRUCT 1 AS b FROM x) AS y FROM z",
+                "bigquery": "SELECT AS STRUCT ARRAY(SELECT AS STRUCT 1 AS b FROM x) AS y FROM z",
+                "duckdb": "SELECT {'y': ARRAY(SELECT {'b': 1} FROM x)} FROM z",
             },
         )
         self.validate_all(
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
index fe7cc3d5..d1848c19 100644
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -10,9 +10,14 @@ class TestDuckDB(Validator):
         self.validate_identity(
             "SELECT * FROM x LEFT JOIN UNNEST(y)", "SELECT * FROM x LEFT JOIN UNNEST(y) ON TRUE"
         )
-        struct_pack = parse_one('STRUCT_PACK("a b" := 1)', read="duckdb")
-        self.assertIsInstance(struct_pack.expressions[0].this, exp.Identifier)
-        self.assertEqual(struct_pack.sql(dialect="duckdb"), "{'a b': 1}")
+        self.validate_all(
+            'STRUCT_PACK("a b" := 1)',
+            write={
+                "duckdb": "{'a b': 1}",
+                "spark": "STRUCT(1 AS `a b`)",
+                "snowflake": "OBJECT_CONSTRUCT('a b', 1)",
+            },
+        )
 
         self.validate_all(
             "SELECT SUM(X) OVER (ORDER BY x)",
@@ -55,8 +60,21 @@ class TestDuckDB(Validator):
             exp.select("*").from_("t").offset(exp.select("5").subquery()).sql(dialect="duckdb"),
         )
 
-        for struct_value in ("{'a': 1}", "struct_pack(a := 1)"):
-            self.validate_all(struct_value, write={"presto": UnsupportedError})
+        self.validate_all(
+            "{'a': 1, 'b': '2'}", write={"presto": "CAST(ROW(1, '2') AS ROW(a INTEGER, b VARCHAR))"}
+        )
+        self.validate_all(
+            "struct_pack(a := 1, b := 2)",
+            write={"presto": "CAST(ROW(1, 2) AS ROW(a INTEGER, b INTEGER))"},
+        )
+
+        self.validate_all(
+            "struct_pack(a := 1, b := x)",
+            write={
+                "duckdb": "{'a': 1, 'b': x}",
+                "presto": UnsupportedError,
+            },
+        )
 
         for join_type in ("SEMI", "ANTI"):
             exists = "EXISTS" if join_type == "SEMI" else "NOT EXISTS"
diff --git a/tests/dialects/test_hive.py b/tests/dialects/test_hive.py
index ea28f293..3c921009 100644
--- a/tests/dialects/test_hive.py
+++ b/tests/dialects/test_hive.py
@@ -611,12 +611,6 @@ class TestHive(Validator):
                 "spark": "GET_JSON_OBJECT(x, '$.name')",
             },
         )
-        self.validate_all(
-            "STRUCT(a = b, c = d)",
-            read={
-                "snowflake": "OBJECT_CONSTRUCT(a, b, c, d)",
-            },
-        )
         self.validate_all(
             "MAP(a, b, c, d)",
             read={
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
index a8cc584d..b3d42f30 100644
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -372,15 +372,17 @@ WHERE
             write={"snowflake": "SELECT * FROM (VALUES (0)) AS foo(bar)"},
         )
         self.validate_all(
-            "OBJECT_CONSTRUCT(a, b, c, d)",
+            "OBJECT_CONSTRUCT('a', b, 'c', d)",
             read={
-                "": "STRUCT(a as b, c as d)",
+                "": "STRUCT(b as a, d as c)",
             },
             write={
                 "duckdb": "{'a': b, 'c': d}",
-                "snowflake": "OBJECT_CONSTRUCT(a, b, c, d)",
+                "snowflake": "OBJECT_CONSTRUCT('a', b, 'c', d)",
             },
         )
+        self.validate_identity("OBJECT_CONSTRUCT(a, b, c, d)")
+
         self.validate_all(
             "SELECT i, p, o FROM qt QUALIFY ROW_NUMBER() OVER (PARTITION BY p ORDER BY o) = 1",
             write={