This is an automated email from the ASF dual-hosted git repository.
morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new bb6be98ffa6 [fix](fe) Send canonical time zone IDs to backends and
connectors (#68865)
bb6be98ffa6 is described below
commit bb6be98ffa691532a704600cd2fde8d9c1fbb2f7
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Mon Oct 12 13:31:45 2026 +0800
[fix](fe) Send canonical time zone IDs to backends and connectors (#68865)
### What problem does this PR solve?
Issue Number: None
Related PR: #65868
Problem Summary:
**In short.** This ports the time-zone part of branch-4.1 #65868.
`time_zone` accepts Java short IDs such as JST, PST or IST, but the FE
forwards them as written, except for CST. Backends do not know these
names: they silently evaluate time functions in another zone, and Java
code running on a backend fails on them. The FE now sends the canonical
zone ID wherever a time zone leaves the session layer: to backends for
queries, loads, BE constant folding and point queries, and to
connectors.
**Background.** `SET time_zone` validates the name against
`TimeUtils.timeZoneAliasMap`, which is `ZoneId.SHORT_IDS` plus the Doris
overrides (CST and PRC map to Asia/Shanghai, UTC and GMT to UTC), and
stores the name as written. When the FE evaluates time itself, it
resolves the name through that map. The places that hand the time zone
on behave differently:
| Where the time zone goes | master before this PR |
|---|---|
| Query globals (`CoordinatorContext`), BE constant folding,
short-circuit point queries | raw name, CST special-cased to
Asia/Shanghai |
| Broker load and export (`setQueryGlobalsForLoad`), stream and routine
load (`NereidsStreamLoadPlanner`) | raw name |
| Connectors (`ConnectorSessionBuilder`) | raw name |
branch-4.1 #65868 added `TimeUtils.getCanonicalTimeZone`
(`ZoneId.of(name, timeZoneAliasMap).getId()`) and used it for the query,
constant-folding and point-query paths. master does not have it.
**The problem, and what it cost.**
- **Wrong results, no error.** A backend looks the name up in its tz
database, which has only EST, HST and MST of the 28 Java short IDs. For
the others (JST, PST, IST, CTT and 20 more; CST is special-cased),
`RuntimeState` ignores the failed lookup and keeps UTC, and a
short-circuit point query falls back to the backend's default zone.
Constant expressions folded on the FE are still right, so one query can
mix two zones.
- **Loads convert in the wrong zone.** A stream load or broker load
whose `timezone` is such a name converts with the same fallback.
- **Java code on the backend fails.** It resolves the name with
`ZoneId.of`, which rejects short IDs. A Paimon INSERT under JST fails
with `ZoneRulesException: Unknown time-zone ID: JST`.
- **Connectors get the raw name.** The Iceberg and Paimon connectors
carry their own copies of the alias map. The MaxCompute predicate
converter does not, so for a session in CST it stops pushing down
datetime predicates.
**How this PR fixes it.**
- Adds `TimeUtils.getCanonicalTimeZone`, as in branch-4.1.
- Applies it in `CoordinatorContext.refreshQueryGlobals` (queries, group
commit, jobs with a session), `createQueryGlobalsWithoutSession` (jobs
started by FE daemons), `setQueryGlobalsForLoad` (broker load, export),
`NereidsStreamLoadPlanner` (stream and routine load, transactional
INSERT), `FoldConstantRuleOnBE`, `PointQueryExecutor` and
`ConnectorSessionBuilder`. The CST special cases go away, because the
alias map already resolves CST to Asia/Shanghai.
- Names a backend already understands are unchanged (`Asia/Tokyo`,
`UTC`, `+08:00`, `GMT+08:00`). Short IDs become tz database names or
offsets: JST becomes `Asia/Tokyo`, PST `America/Los_Angeles`, EST
`-05:00`.
- A zone that is UTC at every instant goes out as `UTC`. Java names
`+00:00` `Z` and `GMT+00:00` a bare `GMT`, which a backend resolves only
from the optional `Zulu` and `GMT` zone files (Debian 13's `tzdata` has
no `Zulu`); `UTC` needs no zone file.
- The other time zones a backend receives need no change.
`hive.parquet.time-zone` rejects short IDs when the catalog or TVF is
created. The zones a backend hands on, to Java scanners and writers,
Arrow Flight results and partial updates, come from the query globals.
**Results.** Local FE + BE, a row with `ts` = 0, `time_zone` = JST:
| | before | after |
|---|---|---|
| `select from_unixtime(ts) from t` | 1970-01-01 00:00:00 | 1970-01-01
09:00:00 |
| the same query as a short-circuit point query | 1970-01-01 08:00:00 |
1970-01-01 09:00:00 |
| stream load with `timezone: JST` and `dt = from_unixtime(ts)` |
1970-01-01 00:00:00 | 1970-01-01 09:00:00 |
| INSERT into a filesystem Paimon table | fails: `Unknown time-zone ID:
JST` | succeeds |
PST, IST and CTT behaved like JST before the fix. EST and CST were
already right.
Co-authored-by: Socrates <[email protected]>
---
.../maxcompute/MaxComputeScanPlanProvider.java | 12 ++--
.../org/apache/doris/common/util/TimeUtils.java | 9 +++
.../doris/connector/ConnectorSessionBuilder.java | 3 +-
.../nereids/load/NereidsStreamLoadPlanner.java | 3 +-
.../expression/rules/FoldConstantRuleOnBE.java | 8 +--
.../org/apache/doris/qe/CoordinatorContext.java | 12 ++--
.../org/apache/doris/qe/PointQueryExecutor.java | 8 +--
.../apache/doris/common/util/TimeUtilsTest.java | 26 ++++++++
.../doris/connector/ConnectorSessionImplTest.java | 12 ++++
.../apache/doris/qe/CoordinatorContextTest.java | 20 +++++++
.../test_session_time_zone_short_ids.out | 31 ++++++++++
.../write/test_paimon_write_append_only.groovy | 11 ++++
.../test_session_time_zone_short_ids.groovy | 69 ++++++++++++++++++++++
13 files changed, 197 insertions(+), 27 deletions(-)
diff --git
a/fe/fe-connector/fe-connector-maxcompute/src/main/java/org/apache/doris/connector/maxcompute/MaxComputeScanPlanProvider.java
b/fe/fe-connector/fe-connector-maxcompute/src/main/java/org/apache/doris/connector/maxcompute/MaxComputeScanPlanProvider.java
index e28bbde6bb7..0c401ee7874 100644
---
a/fe/fe-connector/fe-connector-maxcompute/src/main/java/org/apache/doris/connector/maxcompute/MaxComputeScanPlanProvider.java
+++
b/fe/fe-connector/fe-connector-maxcompute/src/main/java/org/apache/doris/connector/maxcompute/MaxComputeScanPlanProvider.java
@@ -262,12 +262,12 @@ public class MaxComputeScanPlanProvider implements
ConnectorScanPlanProvider {
// Source time zone = the session time zone, mirroring legacy
// MaxComputeScanNode.convertDateTimezone's DateUtils.getTimeZone() (=
the session var).
- // ConnectorSession.getTimeZone() is populated from
ctx.getSessionVariable().getTimeZone()
- // by ConnectorSessionBuilder.from(ctx), so this is the same source as
legacy. (The earlier
- // project-region TZ from the endpoint was wrong: Doris interprets
datetime literals in the
- // session TZ, so converting from any other zone shifts the
pushed-down UTC literal.) The id
- // is passed raw and parsed lazily inside the converter, so a
Doris-valid-but-ZoneId-invalid
- // value (e.g. "CST") degrades the datetime predicate instead of
failing the query.
+ // ConnectorSessionBuilder.from(ctx) fills
ConnectorSession.getTimeZone() with the canonical
+ // id of ctx.getSessionVariable().getTimeZone() (CST becomes
Asia/Shanghai, as legacy resolved
+ // it), so this is the same zone as legacy. (The earlier
project-region TZ from the endpoint
+ // was wrong: Doris interprets datetime literals in the session TZ, so
converting from any
+ // other zone shifts the pushed-down UTC literal.) The converter
parses the id lazily, so an
+ // id ZoneId.of rejects degrades the datetime predicate instead of
failing the query.
MaxComputePredicateConverter converter = new
MaxComputePredicateConverter(
columnTypeMap, dateTimePushDown, session.getTimeZone());
return converter.convert(filter.get());
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
index f5e15f67981..bd2bcabc2d8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/util/TimeUtils.java
@@ -39,6 +39,7 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoField;
@@ -142,6 +143,14 @@ public class TimeUtils {
return getTimeZone().toZoneId();
}
+ /** Resolve a Doris time-zone name to the canonical ID understood by
execution backends. */
+ public static String getCanonicalTimeZone(String timeZone) {
+ ZoneId zoneId = ZoneId.of(timeZone, timeZoneAliasMap);
+ // Java names a zero offset Z (+00:00) or a bare GMT (GMT+00:00). A
backend finds those names only in a
+ // zone directory that ships the optional Zulu or GMT file, and it
resolves UTC without one.
+ return zoneId.normalized().equals(ZoneOffset.UTC) ? UTC_TIME_ZONE :
zoneId.getId();
+ }
+
public static TimeZone getUTCTimeZone() {
return TimeZone.getTimeZone(UTC_TIME_ZONE);
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorSessionBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorSessionBuilder.java
index e9cf180dc2d..bdaeae2f4e9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorSessionBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/connector/ConnectorSessionBuilder.java
@@ -19,6 +19,7 @@ package org.apache.doris.connector;
import org.apache.doris.common.Config;
import org.apache.doris.common.util.DebugUtil;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.connector.spi.ConnectorDelegatedCredential;
import org.apache.doris.connector.spi.ConnectorSession;
import org.apache.doris.connector.spi.ConnectorStatementScope;
@@ -74,7 +75,7 @@ public final class ConnectorSessionBuilder {
ConnectorSessionBuilder b = new ConnectorSessionBuilder();
b.queryId = ctx.queryId() != null ? DebugUtil.printId(ctx.queryId()) :
"";
b.user = ctx.getQualifiedUser();
- b.timeZone = ctx.getSessionVariable().getTimeZone();
+ b.timeZone =
TimeUtils.getCanonicalTimeZone(ctx.getSessionVariable().getTimeZone());
b.locale = "en_US"; // Doris doesn't have per-session locale yet
b.sessionProperties = extractSessionProperties(ctx);
b.connectContext = ctx; // read for the delegated credential at
build() time, gated by capability
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
index 78896eae5aa..e3be4cbdfb9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsStreamLoadPlanner.java
@@ -30,6 +30,7 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.Config;
import org.apache.doris.common.DdlException;
import org.apache.doris.common.UserException;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.load.loadv2.LoadTask;
import org.apache.doris.nereids.StatementContext;
import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@@ -326,7 +327,7 @@ public class NereidsStreamLoadPlanner {
params.setQueryOptions(queryOptions);
TQueryGlobals queryGlobals = new TQueryGlobals();
CoordinatorContext.setQueryGlobalsCurrentTime(queryGlobals);
- queryGlobals.setTimeZone(taskInfo.getTimezone());
+
queryGlobals.setTimeZone(TimeUtils.getCanonicalTimeZone(taskInfo.getTimezone()));
if (taskInfo instanceof NereidsRoutineLoadTaskInfo) {
queryGlobals.setLoadZeroTolerance(false);
} else {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
index ff575c05e7f..2c0789e01c4 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnBE.java
@@ -340,12 +340,8 @@ public class FoldConstantRuleOnBE implements
ExpressionPatternRuleFactory {
TQueryGlobals queryGlobals = new TQueryGlobals();
queryGlobals.setNowString(TimeUtils.getDatetimeFormatWithTimeZone().format(LocalDateTime.now()));
queryGlobals.setTimestampMs(System.currentTimeMillis());
- queryGlobals.setTimeZone(TimeUtils.DEFAULT_TIME_ZONE);
- if (context.getSessionVariable().getTimeZone().equals("CST")) {
- queryGlobals.setTimeZone(TimeUtils.DEFAULT_TIME_ZONE);
- } else {
-
queryGlobals.setTimeZone(context.getSessionVariable().getTimeZone());
- }
+ queryGlobals.setTimeZone(
+
TimeUtils.getCanonicalTimeZone(context.getSessionVariable().getTimeZone()));
TQueryOptions tQueryOptions = new TQueryOptions();
tQueryOptions.setBeExecVersion(Config.be_exec_version);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
index 6951a49d456..9e21d760f7c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/CoordinatorContext.java
@@ -358,8 +358,8 @@ public class CoordinatorContext {
public static TQueryGlobals createQueryGlobalsWithoutSession() {
TQueryGlobals queryGlobals = new TQueryGlobals();
setQueryGlobalsCurrentTime(queryGlobals);
- String timeZone =
VariableMgr.getDefaultSessionVariable().getTimeZone();
- queryGlobals.setTimeZone(timeZone.equals("CST") ?
TimeUtils.DEFAULT_TIME_ZONE : timeZone);
+ queryGlobals.setTimeZone(
+
TimeUtils.getCanonicalTimeZone(VariableMgr.getDefaultSessionVariable().getTimeZone()));
queryGlobals.setLoadZeroTolerance(false);
return queryGlobals;
}
@@ -371,7 +371,7 @@ public class CoordinatorContext {
public static void setQueryGlobalsForLoad(
TQueryGlobals queryGlobals, String timezone, boolean
loadZeroTolerance) {
setQueryGlobalsCurrentTime(queryGlobals);
- queryGlobals.setTimeZone(timezone);
+ queryGlobals.setTimeZone(TimeUtils.getCanonicalTimeZone(timezone));
queryGlobals.setLoadZeroTolerance(loadZeroTolerance);
}
@@ -383,11 +383,7 @@ public class CoordinatorContext {
public static void refreshQueryGlobals(TQueryGlobals queryGlobals,
ConnectContext context) {
setQueryGlobalsCurrentTime(queryGlobals,
context.getStartTimeInstant());
- if (context.getSessionVariable().getTimeZone().equals("CST")) {
- queryGlobals.setTimeZone(TimeUtils.DEFAULT_TIME_ZONE);
- } else {
-
queryGlobals.setTimeZone(context.getSessionVariable().getTimeZone());
- }
+
queryGlobals.setTimeZone(TimeUtils.getCanonicalTimeZone(context.getSessionVariable().getTimeZone()));
queryGlobals.setLcTimeNames(context.getSessionVariable().getLcTimeNames());
}
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 f61c7ffae09..0b51c6bcb10 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
@@ -33,6 +33,7 @@ import org.apache.doris.cloud.catalog.CloudPartition;
import org.apache.doris.common.Config;
import org.apache.doris.common.Status;
import org.apache.doris.common.UserException;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.mysql.MysqlCommand;
import org.apache.doris.nereids.StatementContext;
import org.apache.doris.nereids.exceptions.AnalysisException;
@@ -425,11 +426,8 @@ public class PointQueryExecutor implements CoordInterface {
}
// Set timezone for functions like from_unixtime
- String timeZone =
ConnectContext.get().getSessionVariable().getTimeZone();
- if ("CST".equals(timeZone)) {
- timeZone = "Asia/Shanghai";
- }
- requestBuilder.setTimeZone(timeZone);
+ requestBuilder.setTimeZone(TimeUtils.getCanonicalTimeZone(
+ ConnectContext.get().getSessionVariable().getTimeZone()));
if (snapshotVisibleVersions != null &&
!snapshotVisibleVersions.isEmpty()) {
requestBuilder.setVersion(snapshotVisibleVersions.get(0));
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
index f69ab124c1f..e21950bee4f 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/common/util/TimeUtilsTest.java
@@ -181,6 +181,32 @@ public class TimeUtilsTest {
}
}
+ @Test
+ public void testCanonicalTimeZone() {
+ // A Java short ID is a valid time_zone, but a backend only knows tz
database names and offsets, and the
+ // Java scanners and writers it runs resolve the name with a plain
ZoneId.of. MUTATION: passing the name
+ // through unchanged -> the short IDs below stay as they are -> red.
+ Assertions.assertEquals("Asia/Shanghai",
TimeUtils.getCanonicalTimeZone("CST"));
+ Assertions.assertEquals("-05:00",
TimeUtils.getCanonicalTimeZone("EST"));
+ Assertions.assertEquals("Asia/Tokyo",
TimeUtils.getCanonicalTimeZone("JST"));
+ Assertions.assertEquals("America/Los_Angeles",
TimeUtils.getCanonicalTimeZone("PST"));
+ Assertions.assertEquals("UTC", TimeUtils.getCanonicalTimeZone("GMT"));
+ // Names a backend already understands pass through unchanged.
+ Assertions.assertEquals("Asia/Tokyo",
TimeUtils.getCanonicalTimeZone("Asia/Tokyo"));
+ Assertions.assertEquals("UTC", TimeUtils.getCanonicalTimeZone("UTC"));
+ Assertions.assertEquals("+08:00",
TimeUtils.getCanonicalTimeZone("+08:00"));
+ Assertions.assertEquals("GMT+08:00",
TimeUtils.getCanonicalTimeZone("GMT+08:00"));
+ // Java names a zero offset Z or a bare GMT, which a backend resolves
only from the optional Zulu or GMT
+ // zone file (Debian 13's tzdata ships no Zulu). MUTATION: returning
ZoneId.getId() for these -> red.
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("+00:00"));
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("-00:00"));
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("GMT+00:00"));
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("GMT-00:00"));
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("UTC+00:00"));
+ Assertions.assertEquals("UTC",
TimeUtils.getCanonicalTimeZone("Etc/UTC"));
+ Assertions.assertEquals("-00:30",
TimeUtils.getCanonicalTimeZone("-00:30"));
+ }
+
@Test
public void testGetHourAsDate() {
Calendar calendar = Calendar.getInstance();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/connector/ConnectorSessionImplTest.java
b/fe/fe-core/src/test/java/org/apache/doris/connector/ConnectorSessionImplTest.java
index a86bc06d5c5..eb2b2b46ce9 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/connector/ConnectorSessionImplTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/connector/ConnectorSessionImplTest.java
@@ -313,6 +313,18 @@ public class ConnectorSessionImplTest {
Assertions.assertFalse(session.getDelegatedCredential().isPresent());
}
+ @Test
+ public void sessionTimeZoneReachesConnectorsAsCanonicalId() {
+ // Connectors resolve the session time zone with ZoneId.of, which
rejects Doris aliases such as CST or JST
+ // (MaxCompute then stops pushing down datetime predicates). MUTATION:
copying the session value as is ->
+ // red.
+ ConnectContext ctx = new ConnectContext();
+ ctx.getSessionVariable().setTimeZone("JST");
+ Assertions.assertEquals("Asia/Tokyo",
ConnectorSessionBuilder.from(ctx).build().getTimeZone());
+ ctx.getSessionVariable().setTimeZone("CST");
+ Assertions.assertEquals("Asia/Shanghai",
ConnectorSessionBuilder.from(ctx).build().getTimeZone());
+ }
+
@Test
public void explicitNoneStatementScopeWinsOverLiveContext() {
// Callers that deliberately request NONE must not accidentally
inherit a live statement scope. Current
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/qe/CoordinatorContextTest.java
b/fe/fe-core/src/test/java/org/apache/doris/qe/CoordinatorContextTest.java
index 8c2ff6cb49f..9ecb6e7f68b 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/qe/CoordinatorContextTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/qe/CoordinatorContextTest.java
@@ -72,6 +72,26 @@ class CoordinatorContextTest {
Assertions.assertFalse(queryGlobals.isLoadZeroTolerance());
}
+ // A backend resolves the time zone against its tz database, which has no
Java short IDs: it ran a query or a
+ // load under JST in the wrong zone, and a Java writer failed on
ZoneId.of("JST"). MUTATION: setting the name
+ // as the session or the load gives it -> red.
+ @Test
+ void testQueryGlobalsCarryCanonicalTimeZones() {
+ ConnectContext context = new ConnectContext();
+ context.getSessionVariable().setTimeZone("JST");
+ TQueryGlobals queryGlobals = new TQueryGlobals();
+ CoordinatorContext.refreshQueryGlobals(queryGlobals, context);
+ Assertions.assertEquals("Asia/Tokyo", queryGlobals.getTimeZone());
+
+ context.getSessionVariable().setTimeZone("CST");
+ CoordinatorContext.refreshQueryGlobals(queryGlobals, context);
+ Assertions.assertEquals("Asia/Shanghai", queryGlobals.getTimeZone());
+
+ TQueryGlobals loadGlobals = new TQueryGlobals();
+ CoordinatorContext.setQueryGlobalsForLoad(loadGlobals, "PST", false);
+ Assertions.assertEquals("America/Los_Angeles",
loadGlobals.getTimeZone());
+ }
+
@Test
void testLoadQueryGlobalsSetNanoseconds() {
TQueryGlobals queryGlobals = new TQueryGlobals();
diff --git
a/regression-test/data/query_p0/session_variable/test_session_time_zone_short_ids.out
b/regression-test/data/query_p0/session_variable/test_session_time_zone_short_ids.out
new file mode 100644
index 00000000000..e676fa1268d
--- /dev/null
+++
b/regression-test/data/query_p0/session_variable/test_session_time_zone_short_ids.out
@@ -0,0 +1,31 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !scan --
+Asia/Tokyo 1970-01-01 09:00:00
+
+-- !scan --
+JST 1970-01-01 09:00:00
+
+-- !scan --
+PST 1969-12-31 16:00:00
+
+-- !scan --
+IST 1970-01-01 05:30:00
+
+-- !scan --
+CTT 1970-01-01 08:00:00
+
+-- !scan --
+EST 1969-12-31 19:00:00
+
+-- !scan --
+CST 1970-01-01 08:00:00
+
+-- !point --
+1 1970-01-01 09:00:00
+
+-- !load_status --
+Success 1
+
+-- !load --
+1 0 1970-01-01T09:00
+
diff --git
a/regression-test/suites/external_table_p0/paimon/write/test_paimon_write_append_only.groovy
b/regression-test/suites/external_table_p0/paimon/write/test_paimon_write_append_only.groovy
index d2a25feb65a..8df4522a5a2 100644
---
a/regression-test/suites/external_table_p0/paimon/write/test_paimon_write_append_only.groovy
+++
b/regression-test/suites/external_table_p0/paimon/write/test_paimon_write_append_only.groovy
@@ -70,6 +70,11 @@ suite("test_paimon_write_append_only", "p0,external,paimon")
{
id INT, name STRING, dt STRING NOT NULL DEFAULT '2026-07-01'
) USING paimon
PARTITIONED BY (dt);
+
+ DROP TABLE IF EXISTS paimon.${dbName}.t_append_time_zone;
+ CREATE TABLE paimon.${dbName}.t_append_time_zone (
+ id INT, name STRING
+ ) USING paimon;
"""
sql """drop catalog if exists ${catalogName}"""
@@ -212,6 +217,12 @@ suite("test_paimon_write_append_only",
"p0,external,paimon") {
sql """INSERT INTO t_append (id, ID) VALUES (8, 9)"""
exception "Column 'ID' specified twice"
}
+
+ // A Java short time-zone ID such as JST reaches the JNI writer as its
canonical zone ID.
+ sql """set time_zone = 'JST'"""
+ sql """INSERT INTO t_append_time_zone VALUES (1, 'alice')"""
+ sql """set time_zone = default"""
+ assertTableEquals("t_append_time_zone", "ORDER BY id")
} finally {
sql """drop catalog if exists ${catalogName}"""
}
diff --git
a/regression-test/suites/query_p0/session_variable/test_session_time_zone_short_ids.groovy
b/regression-test/suites/query_p0/session_variable/test_session_time_zone_short_ids.groovy
new file mode 100644
index 00000000000..3fb54544b87
--- /dev/null
+++
b/regression-test/suites/query_p0/session_variable/test_session_time_zone_short_ids.groovy
@@ -0,0 +1,69 @@
+// 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.
+
+
+// time_zone accepts Java short IDs such as JST or PST, which the backend tz
database does not have. The frontend
+// must hand backends the canonical zone ID; otherwise a backend computes in a
fallback zone without any error.
+suite("test_session_time_zone_short_ids") {
+ sql "drop table if exists test_session_time_zone_short_ids"
+ sql """
+ create table test_session_time_zone_short_ids (k int, ts bigint)
+ unique key(k) distributed by hash(k) buckets 1
+ properties ("replication_num" = "1",
"enable_unique_key_merge_on_write" = "true",
+ "store_row_column" = "true")
+ """
+ sql "insert into test_session_time_zone_short_ids values (1, 0)"
+
+ // from_unixtime runs on the backend, in the time zone of the query.
+ for (String tz : ["Asia/Tokyo", "JST", "PST", "IST", "CTT", "EST", "CST"])
{
+ sql "set time_zone = '${tz}'"
+ order_qt_scan "select '${tz}', from_unixtime(ts) from
test_session_time_zone_short_ids"
+ }
+
+ // A short-circuit point query sends the time zone in its own request.
+ sql "set time_zone = 'JST'"
+ sql "set enable_short_circuit_query = true"
+ explain {
+ sql "select k, from_unixtime(ts) from test_session_time_zone_short_ids
where k = 1"
+ contains "SHORT-CIRCUIT"
+ }
+ order_qt_point "select k, from_unixtime(ts) from
test_session_time_zone_short_ids where k = 1"
+ sql "set time_zone = default"
+
+ // A stream load carries its own time zone.
+ sql "drop table if exists test_session_time_zone_short_ids_load"
+ sql """
+ create table test_session_time_zone_short_ids_load (k int, ts bigint,
dt datetime)
+ duplicate key(k) distributed by hash(k) buckets 1
+ properties ("replication_num" = "1")
+ """
+ streamLoad {
+ table "test_session_time_zone_short_ids_load"
+ set "column_separator", ","
+ set "timezone", "JST"
+ set "columns", "k, ts, dt = from_unixtime(ts)"
+ inputText "1,0\n"
+ check { result, exception, startTime, endTime ->
+ if (exception != null) {
+ throw exception
+ }
+ def json = parseJson(result)
+ qt_load_status "select '${json.Status}', ${json.NumberLoadedRows}"
+ }
+ }
+ order_qt_load "select k, ts, dt from test_session_time_zone_short_ids_load"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]