diff --git a/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/CoralToTrinoSqlCallConverter.java b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/CoralToTrinoSqlCallConverter.java index 142360ff4..94bff7574 100644 --- a/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/CoralToTrinoSqlCallConverter.java +++ b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/CoralToTrinoSqlCallConverter.java @@ -34,6 +34,7 @@ import com.linkedin.coral.trino.rel2trino.transformers.ReturnTypeAdjustmentTransformer; import com.linkedin.coral.trino.rel2trino.transformers.SqlSelectAliasAppenderTransformer; import com.linkedin.coral.trino.rel2trino.transformers.ToDateOperatorTransformer; +import com.linkedin.coral.trino.rel2trino.transformers.UnixTimestampOperatorTransformer; import com.linkedin.coral.trino.rel2trino.transformers.UnnestOperatorTransformer; import static com.linkedin.coral.trino.rel2trino.CoralTrinoConfigKeys.*; @@ -106,6 +107,7 @@ protected SqlCall transform(SqlCall sqlCall) { null, null), new ToDateOperatorTransformer(configs.getOrDefault(AVOID_TRANSFORM_TO_DATE_UDF, false)), new CurrentTimestampTransformer(), new FromUnixtimeOperatorTransformer(), + new UnixTimestampOperatorTransformer(), // LinkedIn specific functions new CoralRegistryOperatorRenameSqlCallTransformer( diff --git a/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/UnixTimestampOperatorTransformer.java b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/UnixTimestampOperatorTransformer.java new file mode 100644 index 000000000..ce4154752 --- /dev/null +++ b/coral-trino/src/main/java/com/linkedin/coral/trino/rel2trino/transformers/UnixTimestampOperatorTransformer.java @@ -0,0 +1,80 @@ +/** + * Copyright 2023 LinkedIn Corporation. All rights reserved. + * Licensed under the BSD-2 Clause license. + * See LICENSE in the project root for license information. + */ +package com.linkedin.coral.trino.rel2trino.transformers; + +import java.util.List; + +import org.apache.calcite.sql.*; +import org.apache.calcite.sql.fun.SqlStdOperatorTable; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.sql.type.OperandTypes; +import org.apache.calcite.sql.type.SqlSingleOperandTypeChecker; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.NlsString; + +import com.linkedin.coral.common.transformers.SqlCallTransformer; + +import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CURRENT_TIMESTAMP; +import static org.apache.calcite.sql.type.ReturnTypes.explicit; +import static org.apache.calcite.sql.type.SqlTypeName.DOUBLE; + + +/** + * This class implements the transformation from the operation of "unix_timestamp()、unix_timestamp(string date)、unix_timestamp(string date, string pattern)" + */ +public class UnixTimestampOperatorTransformer extends SqlCallTransformer { + private static final String UNIX_TIMESTAMP_FUNCTION_NAME = "unix_timestamp"; + + @Override + protected boolean condition(SqlCall sqlCall) { + return sqlCall.getOperator().getName().equalsIgnoreCase(UNIX_TIMESTAMP_FUNCTION_NAME); + } + + @Override + protected SqlCall transform(SqlCall sqlCall) { + List operandList = sqlCall.getOperandList(); + SqlSingleOperandTypeChecker sqlSingleOperandTypeChecker = + OperandTypes.or(OperandTypes.POSITIVE_INTEGER_LITERAL, OperandTypes.NILADIC); + SqlFunction sqlFunction = CURRENT_TIMESTAMP; + if (operandList != null && operandList.size() > 0) { + int size = operandList.size(); + if (size == 2) { + SqlNode sqlNode = operandList.get(1); + if (sqlNode instanceof SqlCharStringLiteral) { + SqlCharStringLiteral sqlCharStringLiteral = (SqlCharStringLiteral) sqlNode; + NlsString value = (NlsString) (sqlCharStringLiteral.getValue()); + String sourceFormat = value.getValue(); + // hive https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html + // trino https://trino.io/docs/current/functions/datetime.html#date + String formatNew = + sourceFormat.replaceAll("yyyy", "%Y").replaceAll("MM", "%m").replaceAll("LL", "%M").replaceAll("dd", "%d") + .replaceAll("HH", "%H").replaceAll("hh", "%h").replaceAll("mm", "%i").replaceAll("ss", "%s"); + SqlCharStringLiteral sqlCharStringLiteralNew = + SqlLiteral.createCharString(formatNew, value.getCharsetName(), sqlCharStringLiteral.getParserPosition()); + sqlCall.setOperand(1, sqlCharStringLiteralNew); + sqlFunction = new SqlFunction("date_parse", SqlKind.OTHER_FUNCTION, null, null, sqlSingleOperandTypeChecker, + SqlFunctionCategory.TIMEDATE); + } + } else if (size == 1) { + String formatNew = "%Y-%m-%d %H:%i:%s"; + SqlCharStringLiteral sqlCharStringLiteralNew = SqlLiteral.createCharString(formatNew, SqlParserPos.ZERO); + SqlNode[] sqlNodes = { operandList.get(0), sqlCharStringLiteralNew }; + sqlCall = new SqlBasicCall(sqlCall.getOperator(), sqlNodes, sqlCall.getParserPosition()); + sqlFunction = new SqlFunction("date_parse", SqlKind.OTHER_FUNCTION, null, null, sqlSingleOperandTypeChecker, + SqlFunctionCategory.TIMEDATE); + } + } + + SqlDataTypeSpec timestampType = + new SqlDataTypeSpec(new SqlBasicTypeNameSpec(SqlTypeName.TIMESTAMP, 0, SqlParserPos.ZERO), SqlParserPos.ZERO); + SqlBasicCall sqlBasicCall = + new SqlBasicCall(sqlFunction, ((SqlBasicCall) sqlCall).operands, sqlCall.getParserPosition()); + SqlCall call = SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO, sqlBasicCall, timestampType); + SqlFunction toUnixTimeFun = new SqlFunction("to_unixtime", SqlKind.OTHER_FUNCTION, explicit(DOUBLE), null, + sqlSingleOperandTypeChecker, SqlFunctionCategory.TIMEDATE); + return toUnixTimeFun.createCall(SqlParserPos.ZERO, call); + } +} 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 6c90a42a0..37687de6c 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 @@ -165,6 +165,9 @@ public Object[][] viewTestCasesProvider() { { "test", "date_calculation_view", "SELECT \"date\"(CAST(\"substr\"('2021-08-20', 1, 10) AS TIMESTAMP)), \"date\"(CAST('2021-08-20' AS TIMESTAMP)), \"date\"(CAST('2021-08-20 00:00:00' AS TIMESTAMP)), \"date_add\"('day', 1, \"date\"(CAST('2021-08-20' AS TIMESTAMP))), \"date_add\"('day', 1, \"date\"(CAST('2021-08-20 00:00:00' AS TIMESTAMP))), \"date_add\"('day', 1 * -1, \"date\"(CAST('2021-08-20' AS TIMESTAMP))), \"date_add\"('day', 1 * -1, \"date\"(CAST('2021-08-20 00:00:00' AS TIMESTAMP))), CAST(\"date_diff\"('day', \"date\"(CAST('2021-08-21' AS TIMESTAMP)), \"date\"(CAST('2021-08-20' AS TIMESTAMP))) AS INTEGER), CAST(\"date_diff\"('day', \"date\"(CAST('2021-08-19' AS TIMESTAMP)), \"date\"(CAST('2021-08-20' AS TIMESTAMP))) AS INTEGER), CAST(\"date_diff\"('day', \"date\"(CAST('2021-08-19 23:59:59' AS TIMESTAMP)), \"date\"(CAST('2021-08-20 00:00:00' AS TIMESTAMP))) AS INTEGER)\n" + "FROM \"test\".\"tablea\" AS \"tablea\"" }, + { "test", "unix_timestamp_view", "SELECT TO_UNIXTIME(CAST(CURRENT_TIMESTAMP AS TIMESTAMP(0))), TO_UNIXTIME(CAST(DATE_PARSE('2023-06-14 00:00:00', '%Y-%m-%d %H:%i:%s') AS TIMESTAMP(0))), TO_UNIXTIME(CAST(DATE_PARSE('20230614', '%Y%m%d') AS TIMESTAMP(0)))\n" + + "FROM \"test\".\"tablea\" AS \"tablea\"" }, + { "test", "pmod_view", "SELECT MOD(MOD(- 9, 4) + 4, 4)\n" + "FROM \"test\".\"tablea\" AS \"tablea\"" }, { "test", "nullscollationd_view", "SELECT *\n" + "FROM \"test\".\"tabler\" AS \"tabler\"\n" diff --git a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/TestUtils.java b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/TestUtils.java index 2c6bb0f97..3e071faa8 100644 --- a/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/TestUtils.java +++ b/coral-trino/src/test/java/com/linkedin/coral/trino/rel2trino/TestUtils.java @@ -343,6 +343,11 @@ public static void initializeTablesAndViews(HiveConf conf) throws HiveException, + "datediff('2021-08-20', '2021-08-19'), " + "datediff('2021-08-20 00:00:00', '2021-08-19 23:59:59')" + "FROM test.tableA"); + run(driver, + "CREATE VIEW IF NOT EXISTS test.unix_timestamp_view AS \n" + + "SELECT unix_timestamp(), unix_timestamp('2023-06-14 00:00:00'), unix_timestamp('20230614','yyyyMMdd')" + + "FROM test.tableA"); + run(driver, "CREATE VIEW IF NOT EXISTS test.pmod_view AS \n" + "SELECT pmod(-9, 4) FROM test.tableA"); run(driver, "CREATE TABLE IF NOT EXISTS test.tableR(a int, b string, c int)");