From c55e0542c93d07e0df494892943f6bf757caf236 Mon Sep 17 00:00:00 2001 From: Mryange Date: Mon, 17 Aug 2026 15:24:58 +0800 Subject: [PATCH] [fix](fe) Preserve TIMESTAMPTZ user variable type ### What problem does this PR solve? Issue Number: N/A Problem Summary: TIMESTAMPTZ user variables lost their original type when converted from legacy literals to Nereids literals. Preserve DateLiteral types through the existing legacy literal conversion path so timezone rendering and comparison semantics remain correct. ### Release note Fix TIMESTAMPTZ user variables to preserve timezone-aware rendering and comparison semantics. ### Check List (For Author) - Test: FE Unit Test, Regression test, and Manual test - Behavior changed: Yes. TIMESTAMPTZ user variables retain their type and timezone semantics. - Does this need documentation: No --- .../org/apache/doris/qe/ConnectContext.java | 3 +++ .../analysis/UserVariableAnalysisTest.java | 17 +++++++++++++++++ .../timestamptz/test_timestamptz_dst_fold.out | 7 +++++++ .../test_timestamptz_dst_fold.groovy | 11 +++++++++++ 4 files changed, 38 insertions(+) diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index 2b910dfa796113..057f21f85bd7ae 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -18,6 +18,7 @@ package org.apache.doris.qe; import org.apache.doris.analysis.BoolLiteral; +import org.apache.doris.analysis.DateLiteral; import org.apache.doris.analysis.DecimalLiteral; import org.apache.doris.analysis.FloatLiteral; import org.apache.doris.analysis.IntLiteral; @@ -670,6 +671,8 @@ public void setUserVar(String name, LiteralExpr value) { return Literal.of(((FloatLiteral) literalExpr).getValue()); } else if (literalExpr instanceof DecimalLiteral) { return Literal.of(((DecimalLiteral) literalExpr).getValue()); + } else if (literalExpr instanceof DateLiteral) { + return Literal.fromLegacyLiteral(literalExpr, literalExpr.getType()); } else if (literalExpr instanceof StringLiteral) { return Literal.of(((StringLiteral) literalExpr).getValue()); } else if (literalExpr instanceof NullLiteral) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java index e7ea8b491eafa8..2dd8ca07be5403 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java @@ -17,12 +17,17 @@ package org.apache.doris.nereids.rules.analysis; +import org.apache.doris.analysis.DateLiteral; import org.apache.doris.analysis.IntLiteral; import org.apache.doris.analysis.LargeIntLiteral; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.IntegerType; import org.apache.doris.nereids.types.LargeIntType; import org.apache.doris.nereids.types.SmallIntType; +import org.apache.doris.nereids.types.TimeStampTzType; import org.apache.doris.nereids.types.TinyIntType; import org.apache.doris.nereids.util.MemoTestUtils; import org.apache.doris.qe.ConnectContext; @@ -53,4 +58,16 @@ public void testUserVarIntegerType() { Assertions.assertEquals(BigIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("d").getDataType()); Assertions.assertEquals(LargeIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("e").getDataType()); } + + @Test + public void testUserVarTimestampTzType() { + ConnectContext ctx = MemoTestUtils.createConnectContext(); + ctx.setUserVar("ts", new DateLiteral( + 2024, 11, 3, 5, 5, 0, 123456, ScalarType.createTimeStampTzType(6))); + + Literal literal = ctx.getLiteralForUserVar("ts"); + Assertions.assertInstanceOf(TimestampTzLiteral.class, literal); + Assertions.assertEquals(TimeStampTzType.of(6), literal.getDataType()); + Assertions.assertEquals("2024-11-03 05:05:00.123456+00:00", literal.getStringValue()); + } } diff --git a/regression-test/data/datatype_p0/timestamptz/test_timestamptz_dst_fold.out b/regression-test/data/datatype_p0/timestamptz/test_timestamptz_dst_fold.out index a2dba24a8029f3..ab8c18ca9cbfba 100644 --- a/regression-test/data/datatype_p0/timestamptz/test_timestamptz_dst_fold.out +++ b/regression-test/data/datatype_p0/timestamptz/test_timestamptz_dst_fold.out @@ -1,4 +1,11 @@ -- This file is automatically generated. You should know what you did if you want to edit this +-- !user_var_render -- +2024-11-03 01:05:00.000000-04:00 + +-- !user_var_comparison -- +1 pre_fold_utc +3 pre_explicit + -- !sql -- 1 pre_fold_utc 2024-11-03 01:05:00.000000-04:00 2 post_fold_utc 2024-11-03 01:05:00.000000-05:00 diff --git a/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_dst_fold.groovy b/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_dst_fold.groovy index a090b6f095777d..161bfb84682eb0 100644 --- a/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_dst_fold.groovy +++ b/regression-test/suites/datatype_p0/timestamptz/test_timestamptz_dst_fold.groovy @@ -41,6 +41,17 @@ suite("test_timestamptz_dst_fold") { (4, 'post_explicit', CAST('2024-11-03 01:05:00 -05:00' AS TIMESTAMPTZ(6))); """ + sql "SET time_zone = '+00:00';" + sql "SET @dst_fold_ts = CAST('2024-11-03 05:05:00 +00:00' AS TIMESTAMPTZ(6));" + sql "SET time_zone = 'America/New_York';" + qt_user_var_render "SELECT CAST(@dst_fold_ts AS VARCHAR(64));" + order_qt_user_var_comparison """ + SELECT id, label + FROM tz_dst_fold_events + WHERE ts = @dst_fold_ts + ORDER BY id; + """ + sql "SET debug_skip_fold_constant = true;" qt_sql """ SELECT id, label, CAST(ts AS VARCHAR(64)) AS rendered