diff --git a/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/CurrentTimestampTransformer.java b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/CurrentTimestampTransformer.java index 9fa8139b4..f04940bd8 100644 --- a/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/CurrentTimestampTransformer.java +++ b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/CurrentTimestampTransformer.java @@ -1,5 +1,5 @@ /** - * Copyright 2023 LinkedIn Corporation. All rights reserved. + * Copyright 2023-2024 LinkedIn Corporation. All rights reserved. * Licensed under the BSD-2 Clause license. * See LICENSE in the project root for license information. */ @@ -8,6 +8,10 @@ import org.apache.calcite.sql.SqlBasicTypeNameSpec; import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlDataTypeSpec; +import org.apache.calcite.sql.SqlFunction; +import org.apache.calcite.sql.SqlFunctionCategory; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSyntax; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeName; @@ -17,12 +21,16 @@ /** * This class implements the transformation for the CURRENT_TIMESTAMP function. - * For example, "SELECT CURRENT_TIMESTAMP" is transformed into "SELECT CAST(CURRENT_TIMESTAMP AS TIMESTAMP(3))". - * This transformation ensures compatibility with Trino. + * For example, "SELECT CURRENT_TIMESTAMP" is transformed into "SELECT CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS TIMESTAMP(3))". + * This transformation ensures compatibility with Trino due to the following reasons: + * (1) In Hive, CURRENT_TIMESTAMP returns a UTC timestamp, whereas in Trino, it returns a timestamp based on the session's timezone. + * (2) In Hive, the return type of CURRENT_TIMESTAMP is timestamp, whereas in Trino, the return type is timestamp with time zone, + * so we need to add an explicit CAST. */ public class CurrentTimestampTransformer extends SqlCallTransformer { private static final String CURRENT_TIMESTAMP_FUNCTION_NAME = "CURRENT_TIMESTAMP"; + private static final String CURRENT_TIMESTAMP_AT_TIMEZONE_UTC = "CURRENT_TIMESTAMP AT TIME ZONE 'UTC'"; @Override protected boolean condition(SqlCall sqlCall) { @@ -33,6 +41,17 @@ protected boolean condition(SqlCall sqlCall) { protected SqlCall transform(SqlCall sqlCall) { SqlDataTypeSpec timestampType = new SqlDataTypeSpec(new SqlBasicTypeNameSpec(SqlTypeName.TIMESTAMP, 3, SqlParserPos.ZERO), SqlParserPos.ZERO); - return SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO, sqlCall, timestampType); + final SqlOperator currentTimestampOperator = sqlCall.getOperator(); + final SqlFunction currentTimestampAtTimeZoneUTCOperator = + new SqlFunction(CURRENT_TIMESTAMP_AT_TIMEZONE_UTC, currentTimestampOperator.kind, + currentTimestampOperator.getReturnTypeInference(), currentTimestampOperator.getOperandTypeInference(), + currentTimestampOperator.getOperandTypeChecker(), SqlFunctionCategory.TIMEDATE) { + @Override + public SqlSyntax getSyntax() { + return SqlSyntax.FUNCTION_ID; + } + }; + return SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO, + currentTimestampAtTimeZoneUTCOperator.createCall(SqlParserPos.ZERO, sqlCall.getOperandList()), timestampType); } } diff --git a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/HiveToTrinoConverterTest.java b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/HiveToTrinoConverterTest.java index 328f6a975..69e6e31ab 100644 --- a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/HiveToTrinoConverterTest.java +++ b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/HiveToTrinoConverterTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2017-2023 LinkedIn Corporation. All rights reserved. + * Copyright 2017-2024 LinkedIn Corporation. All rights reserved. * Licensed under the BSD-2 Clause license. * See LICENSE in the project root for license information. */ @@ -136,7 +136,7 @@ public Object[][] viewTestCasesProvider() { { "test", "map_array_view", "SELECT MAP (ARRAY['key1', 'key2'], ARRAY['value1', 'value2']) AS \"simple_map_col\", MAP (ARRAY['key1', 'key2'], ARRAY[MAP (ARRAY['a', 'c'], ARRAY['b', 'd']), MAP (ARRAY['a', 'c'], ARRAY['b', 'd'])]) AS \"nested_map_col\"\n" + "FROM \"test\".\"tablea\" AS \"tablea\"" }, - { "test", "current_date_and_timestamp_view", "SELECT CAST(CURRENT_TIMESTAMP AS TIMESTAMP(3)), TRIM(CAST(CAST(CURRENT_TIMESTAMP AS TIMESTAMP(3)) AS VARCHAR(65535))) AS \"ct\", CURRENT_DATE, CURRENT_DATE AS \"cd\", \"tablea\".\"a\" AS \"a\"\n" + { "test", "current_date_and_timestamp_view", "SELECT CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS TIMESTAMP(3)), TRIM(CAST(CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS TIMESTAMP(3)) AS VARCHAR(65535))) AS \"ct\", CURRENT_DATE, CURRENT_DATE AS \"cd\", \"tablea\".\"a\" AS \"a\"\n" + "FROM \"test\".\"tablea\" AS \"tablea\"" }, { "test", "date_function_view", "SELECT \"date\"('2021-01-02') AS \"a\"\n" diff --git a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/RelToTrinoConverterTest.java b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/RelToTrinoConverterTest.java index 23b19245a..cfde02f56 100644 --- a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/RelToTrinoConverterTest.java +++ b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/RelToTrinoConverterTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2017-2023 LinkedIn Corporation. All rights reserved. + * Copyright 2017-2024 LinkedIn Corporation. All rights reserved. * Licensed under the BSD-2 Clause license. * See LICENSE in the project root for license information. */ @@ -510,8 +510,8 @@ public void testCurrentUser() { @Test public void testCurrentTimestamp() { String sql = "SELECT current_timestamp"; - String expected = - formatSql("SELECT CAST(CURRENT_TIMESTAMP AS TIMESTAMP(3))\nFROM (VALUES (0)) AS \"t\" (\"ZERO\")"); + String expected = formatSql( + "SELECT CAST(CURRENT_TIMESTAMP AT TIME ZONE 'UTC' AS TIMESTAMP(3))\nFROM (VALUES (0)) AS \"t\" (\"ZERO\")"); testConversion(sql, expected); }