From b4f84d9fd169ee9207b06c5b63ebeeb1eba765dc Mon Sep 17 00:00:00 2001 From: lihangyu-x Date: Sat, 28 Feb 2026 17:12:10 +0800 Subject: [PATCH] [fix](point-query) Fix point query ignoring session timezone for functions like from_unixtime Point queries on unique key tables with row store incorrectly used the default timezone (Asia/Shanghai) instead of the session timezone when evaluating timezone-sensitive functions like from_unixtime(). This was because PTabletKeyLookupRequest did not carry timezone information, and the RuntimeState created in Reusable::init() always used the default timezone. The fix adds a time_zone field to PTabletKeyLookupRequest, sets it from the session variable in FE PointQueryExecutor, and applies it to the RuntimeState in BE PointQueryExecutor::init(). Changes: - gensrc/proto: Add time_zone field to PTabletKeyLookupRequest - FE: Send session timezone in PointQueryExecutor.getNextInternal() - BE: Add RuntimeState::set_timezone() and use it in PointQueryExecutor::init() - Add regression test for point query timezone handling --- be/src/runtime/runtime_state.h | 5 ++ be/src/service/point_query_executor.cpp | 4 ++ .../apache/doris/qe/PointQueryExecutor.java | 6 ++ gensrc/proto/internal_service.proto | 2 + .../test_point_query_timezone.out | 15 ++++ .../test_point_query_timezone.groovy | 71 +++++++++++++++++++ 6 files changed, 103 insertions(+) create mode 100644 regression-test/data/point_query_p0/test_point_query_timezone.out create mode 100644 regression-test/suites/point_query_p0/test_point_query_timezone.groovy diff --git a/be/src/runtime/runtime_state.h b/be/src/runtime/runtime_state.h index e07ad2a8a7e3a8..126c8f4f6173e6 100644 --- a/be/src/runtime/runtime_state.h +++ b/be/src/runtime/runtime_state.h @@ -47,6 +47,7 @@ #include "runtime/workload_group/workload_group.h" #include "util/debug_util.h" #include "util/runtime_profile.h" +#include "util/timezone_utils.h" #include "vec/runtime/vector_search_user_params.h" namespace doris { @@ -181,6 +182,10 @@ class RuntimeState { // if possible, use timezone_obj() rather than timezone() const std::string& timezone() const { return _timezone; } const cctz::time_zone& timezone_obj() const { return _timezone_obj; } + void set_timezone(const std::string& timezone) { + _timezone = timezone; + TimezoneUtils::find_cctz_time_zone(_timezone, _timezone_obj); + } const std::string& lc_time_names() const { return _lc_time_names; } const std::string& user() const { return _user; } const TUniqueId& query_id() const { return _query_id; } diff --git a/be/src/service/point_query_executor.cpp b/be/src/service/point_query_executor.cpp index a8f8eadd1f240e..4b908e70c6c159 100644 --- a/be/src/service/point_query_executor.cpp +++ b/be/src/service/point_query_executor.cpp @@ -319,6 +319,10 @@ Status PointQueryExecutor::init(const PTabletKeyLookupRequest* request, *_tablet->tablet_schema(), 1)); } } + // Set timezone from request for functions like from_unixtime() + if (request->has_time_zone() && !request->time_zone().empty()) { + _reusable->runtime_state()->set_timezone(request->time_zone()); + } if (request->has_version() && request->version() >= 0) { _version = request->version(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java index 5925b428a16436..8ddf2487313817 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/PointQueryExecutor.java @@ -278,6 +278,12 @@ private RowBatch getNextInternal(Status status, Backend backend) throws TExcepti .setOutputExpr(shortCircuitQueryContext.serializedOutputExpr) .setQueryOptions(shortCircuitQueryContext.serializedQueryOptions) .setIsBinaryRow(ConnectContext.get().command == MysqlCommand.COM_STMT_EXECUTE); + // Set timezone for functions like from_unixtime + String timeZone = ConnectContext.get().getSessionVariable().getTimeZone(); + if ("CST".equals(timeZone)) { + timeZone = "Asia/Shanghai"; + } + requestBuilder.setTimeZone(timeZone); if (snapshotVisibleVersions != null && !snapshotVisibleVersions.isEmpty()) { requestBuilder.setVersion(snapshotVisibleVersions.get(0)); } diff --git a/gensrc/proto/internal_service.proto b/gensrc/proto/internal_service.proto index f2c0475791e9cb..808a70e327a9ba 100644 --- a/gensrc/proto/internal_service.proto +++ b/gensrc/proto/internal_service.proto @@ -381,6 +381,8 @@ message PTabletKeyLookupRequest { optional int64 version = 7; // serilized from TQueryOptions optional bytes query_options = 8; + // timezone string, e.g. "America/Mexico_City" + optional string time_zone = 9; } message PTabletKeyLookupResponse { diff --git a/regression-test/data/point_query_p0/test_point_query_timezone.out b/regression-test/data/point_query_p0/test_point_query_timezone.out new file mode 100644 index 00000000000000..b0c47ea961d439 --- /dev/null +++ b/regression-test/data/point_query_p0/test_point_query_timezone.out @@ -0,0 +1,15 @@ +-- This file is automatically generated. You should know what you did if you want to edit this +-- !full_scan -- +job_001 2024-01-31 18:00:00.000000 +job_002 2024-02-01 18:00:00.000000 + +-- !point_query -- +job_001 2024-01-31 18:00:00.000000 + +-- !full_scan_tokyo -- +job_001 2024-02-01 09:00:00.000000 +job_002 2024-02-02 09:00:00.000000 + +-- !point_query_tokyo -- +job_001 2024-02-01 09:00:00.000000 + diff --git a/regression-test/suites/point_query_p0/test_point_query_timezone.groovy b/regression-test/suites/point_query_p0/test_point_query_timezone.groovy new file mode 100644 index 00000000000000..927552e8f3cd8f --- /dev/null +++ b/regression-test/suites/point_query_p0/test_point_query_timezone.groovy @@ -0,0 +1,71 @@ +// 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. + +// Test point query timezone handling (cir_19478) +// Point queries on unique key tables with row store should respect +// session timezone for functions like from_unixtime() +suite("test_point_query_timezone") { + def tableName = "test_point_query_timezone_tbl" + + sql """ DROP TABLE IF EXISTS ${tableName} """ + sql """ + CREATE TABLE ${tableName} ( + `job_id` VARCHAR(50) NOT NULL, + `capture_time` BIGINT NOT NULL, + `event_time` BIGINT NULL + ) ENGINE=OLAP + UNIQUE KEY(`job_id`, `capture_time`) + DISTRIBUTED BY HASH(`job_id`) BUCKETS 1 + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1", + "store_row_column" = "true", + "enable_unique_key_merge_on_write" = "true" + ) + """ + + sql """ INSERT INTO ${tableName} VALUES ('job_001', 1706745600000, 1706745600000) """ + sql """ INSERT INTO ${tableName} VALUES ('job_002', 1706832000000, 1706832000000) """ + + // Verify it's a short-circuit (point) query + explain { + sql("SELECT * FROM ${tableName} WHERE job_id = 'job_001' AND capture_time = 1706745600000") + contains "SHORT-CIRCUIT" + } + + // Test with America/Mexico_City timezone (UTC-6) + // 1706745600 = 2024-02-01 00:00:00 UTC = 2024-01-31 18:00:00 Mexico_City + sql """ SET time_zone = 'America/Mexico_City' """ + + // Full table scan - should use session timezone + qt_full_scan """ SELECT job_id, from_unixtime(event_time/1000) AS t FROM ${tableName} ORDER BY job_id """ + + // Point query - should also use session timezone (this was the bug) + qt_point_query """ SELECT job_id, from_unixtime(event_time/1000) AS t FROM ${tableName} WHERE job_id = 'job_001' AND capture_time = 1706745600000 """ + + // Test with Asia/Tokyo timezone (UTC+9) + // 1706745600 = 2024-02-01 00:00:00 UTC = 2024-02-01 09:00:00 Tokyo + sql """ SET time_zone = 'Asia/Tokyo' """ + + qt_full_scan_tokyo """ SELECT job_id, from_unixtime(event_time/1000) AS t FROM ${tableName} ORDER BY job_id """ + + qt_point_query_tokyo """ SELECT job_id, from_unixtime(event_time/1000) AS t FROM ${tableName} WHERE job_id = 'job_001' AND capture_time = 1706745600000 """ + + // Reset timezone + sql """ SET time_zone = 'Asia/Shanghai' """ + + sql """ DROP TABLE IF EXISTS ${tableName} """ +}