From e22683ef880eb8626669777bb4a4385c0cc694c8 Mon Sep 17 00:00:00 2001 From: korlov42 Date: Fri, 19 Jul 2024 17:07:46 +0300 Subject: [PATCH] IGNITE-22780 Sql. Fix cast from VARCHAR to TS WITH LOCAL TZ (#4116) --- .../ItCastToTsWithLocalTimeZoneTest.java | 596 ++++++++++++++++++ .../engine/exec/exp/RexToLixTranslator.java | 5 +- 2 files changed, 600 insertions(+), 1 deletion(-) create mode 100644 modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCastToTsWithLocalTimeZoneTest.java diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCastToTsWithLocalTimeZoneTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCastToTsWithLocalTimeZoneTest.java new file mode 100644 index 00000000000..517a99c903b --- /dev/null +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCastToTsWithLocalTimeZoneTest.java @@ -0,0 +1,596 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.sql.engine; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import org.apache.ignite.internal.sql.BaseSqlIntegrationTest; +import org.apache.ignite.internal.testframework.WithSystemProperty; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Set of tests to ensure correctness of CAST expression to TIMESTAMP WITH TIME ZONE for + * type pairs supported by cast specification. + */ +@WithSystemProperty(key = "IMPLICIT_PK_ENABLED", value = "true") +public class ItCastToTsWithLocalTimeZoneTest extends BaseSqlIntegrationTest { + @BeforeAll + static void createTable() { + sql("CREATE TABLE test (val TIMESTAMP WITH LOCAL TIME ZONE)"); + sql("CREATE TABLE src (id INT PRIMARY KEY, s VARCHAR(100), ts TIMESTAMP, d DATE, t TIME)"); + } + + @AfterAll + static void dropTable() { + sql("DROP TABLE IF EXISTS test"); + sql("DROP TABLE IF EXISTS src"); + } + + @AfterEach + void clearTable() { + sql("DELETE FROM test"); + sql("DELETE FROM src"); + } + + @Test + void implicitCastOfLiteralsOnInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES ('1970-01-01 12:00:00')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (timestamp '1970-01-01 13:00:00')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (date '1970-01-01')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (time '12:00:00')").withTimeZoneId(zone).check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES ('1970-01-01 14:00:00')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (timestamp '1970-01-01 15:00:00')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (date '1970-01-01')").withTimeZoneId(zone).check(); + assertQuery("INSERT INTO test VALUES (time '14:00:00')").withTimeZoneId(zone).check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfLiteralsOnInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES (CAST('1970-01-01 12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(timestamp '1970-01-01 13:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(time '12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES (CAST('1970-01-01 14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(timestamp '1970-01-01 15:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(time '14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfLiteralsOnSelect() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("SELECT CAST('1970-01-01 12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .check(); + assertQuery("SELECT CAST(timestamp '1970-01-01 13:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .check(); + assertQuery("SELECT CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .check(); + assertQuery("SELECT CAST(time '12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("SELECT CAST('1970-01-01 14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .check(); + assertQuery("SELECT CAST(timestamp '1970-01-01 15:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .check(); + assertQuery("SELECT CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .check(); + assertQuery("SELECT CAST(time '14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + } + + @Test + void explicitCastOfLiteralsOnMultiInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES " + + "(CAST('1970-01-01 12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(timestamp '1970-01-01 13:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(time '12:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone).check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES " + + "(CAST('1970-01-01 14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(timestamp '1970-01-01 15:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(date '1970-01-01' as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(time '14:00:00' as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone).check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + @Disabled("https://issues.apache.org/jira/browse/IGNITE-22779") + void implicitCastOfDynParamsOnInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam("1970-01-01 12:00:00") + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T13:00:00")) + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("12:00:00")) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam("1970-01-01 14:00:00") + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T15:00:00")) + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .check(); + assertQuery("INSERT INTO test VALUES (?)") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("14:00:00")) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void implicitCastOfSourceTableOnInsert() { + sql("INSERT INTO src VALUES " + + "(1, '1970-01-01 12:00:00', timestamp '1970-01-01 13:00:00', date '1970-01-01', time '12:00:00')," + + "(2, '1970-01-01 14:00:00', timestamp '1970-01-01 15:00:00', date '1970-01-01', time '14:00:00')" + ); + + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test SELECT s FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT ts FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT d FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT t FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test SELECT s FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT ts FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT d FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT t FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfDynParamsOnInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam("1970-01-01 12:00:00") + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T13:00:00")) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("12:00:00")) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam("1970-01-01 14:00:00") + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T15:00:00")) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .check(); + assertQuery("INSERT INTO test VALUES (CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("14:00:00")) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfDynParamsOnSelect() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam("1970-01-01 12:00:00") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T13:00:00")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("12:00:00")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam("1970-01-01 14:00:00") + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalDateTime.parse("1970-01-01T15:00:00")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalDate.parse("1970-01-01")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .check(); + assertQuery("SELECT CAST(? as TIMESTAMP WITH LOCAL TIME ZONE)") + .withTimeZoneId(zone) + .withParam(LocalTime.parse("14:00:00")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + } + + @Test + void explicitCastOfDynParamsOnMultiInsert() { + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test VALUES " + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParams( + "1970-01-01 12:00:00", + LocalDateTime.parse("1970-01-01T13:00:00"), + LocalDate.parse("1970-01-01"), + LocalTime.parse("12:00:00") + ) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test VALUES " + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))," + + "(CAST(? as TIMESTAMP WITH LOCAL TIME ZONE))") + .withTimeZoneId(zone) + .withParams( + "1970-01-01 14:00:00", + LocalDateTime.parse("1970-01-01T15:00:00"), + LocalDate.parse("1970-01-01"), + LocalTime.parse("14:00:00") + ) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfSourceTableOnInsert() { + sql("INSERT INTO src VALUES " + + "(1, '1970-01-01 12:00:00', timestamp '1970-01-01 13:00:00', date '1970-01-01', time '12:00:00')," + + "(2, '1970-01-01 14:00:00', timestamp '1970-01-01 15:00:00', date '1970-01-01', time '14:00:00')" + ); + + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("INSERT INTO test SELECT CAST(s as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(ts as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(d as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(t as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("INSERT INTO test SELECT CAST(s as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(ts as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(d as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + assertQuery("INSERT INTO test SELECT CAST(t as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .check(); + } + + assertQuery("SELECT * FROM test") + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + + @Test + void explicitCastOfSourceTableOnSelect() { + sql("INSERT INTO src VALUES " + + "(1, '1970-01-01 12:00:00', timestamp '1970-01-01 13:00:00', date '1970-01-01', time '12:00:00')," + + "(2, '1970-01-01 14:00:00', timestamp '1970-01-01 15:00:00', date '1970-01-01', time '14:00:00')" + ); + + { + ZoneId zone = ZoneOffset.ofHours(4); + + assertQuery("SELECT CAST(s as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T08:00:00Z")) + .check(); + + assertQuery("SELECT CAST(ts as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T09:00:00Z")) + .check(); + + assertQuery("SELECT CAST(d as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .returns(Instant.parse("1969-12-31T20:00:00Z")) + .check(); + + assertQuery("SELECT CAST(t as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 1") + .withTimeZoneId(zone) + .returns(Instant.parse(LocalDate.now() + "T08:00:00Z")) + .check(); + } + + { + ZoneId zone = ZoneOffset.ofHours(8); + + assertQuery("SELECT CAST(s as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T06:00:00Z")) + .check(); + + assertQuery("SELECT CAST(ts as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .returns(Instant.parse("1970-01-01T07:00:00Z")) + .check(); + + assertQuery("SELECT CAST(d as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .returns(Instant.parse("1969-12-31T16:00:00Z")) + .check(); + + assertQuery("SELECT CAST(t as TIMESTAMP WITH LOCAL TIME ZONE) FROM src WHERE id = 2") + .withTimeZoneId(zone) + .returns(Instant.parse(LocalDate.now() + "T06:00:00Z")) + .check(); + } + } +} diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java index 9af6157bebf..bb76746f445 100644 --- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java +++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java @@ -406,7 +406,10 @@ Expression translateCast( // Since this type implies a local timezone, its explicit indication seems redundant, // so we prohibit the user from explicitly setting a timezone. convert = - Expressions.call(BuiltInMethod.STRING_TO_TIMESTAMP.method, operand); + Expressions.call( + BuiltInMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE.method, + operand, + Expressions.call(BuiltInMethod.TIME_ZONE.method, root)); break; case DATE: convert =