This is an automated email from the ASF dual-hosted git repository.
morrySnow 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 15add0fc4c4 [fix](partition_prune) Avoid incorrect convert_tz
partition pruning across DST transitions (#63853)
15add0fc4c4 is described below
commit 15add0fc4c415b8dc0b719074e95bb1ae277080b
Author: feiniaofeiafei <[email protected]>
AuthorDate: Mon Jun 8 11:38:15 2026 +0800
[fix](partition_prune) Avoid incorrect convert_tz partition pruning across
DST transitions (#63853)
### What problem does this PR solve?
Related PR: #40047 #64029
Problem Summary:
Nereids currently treats `convert_tz()` as monotonic whenever both
timezone arguments are constant. That assumption is not always correct.
When a partition predicate contains `convert_tz(partition_col, from_tz,
to_tz)`, partition pruning folds the partition range endpoints and
infers a value range for the function. This is only valid if
`convert_tz()` is monotonic on the current partition range.
Across DST transitions, `convert_tz()` is not monotonic:
- during fall-back, different input timestamps can map to the same local
time
- during spring-forward, a local-time gap can break the endpoint-based
range inference
As a result, FE may prune partitions that still contain matching rows.
The reported case is `convert_tz(ts, 'UTC', 'Europe/Paris')` around the
Europe/Paris DST fall-back boundary, where a valid partition is
incorrectly removed.
This PR fixes the issue by tightening the monotonicity check for
`convert_tz()`:
- keep the optimization for fixed-offset timezones
- disable the monotonic shortcut when the source local-time range
touches a DST transition window
- disable the monotonic shortcut when the corresponding target instant
range touches a DST transition
This keeps normal partition pruning behavior for safe cases while
avoiding wrong pruning around DST boundaries.
---
.../expressions/functions/scalar/ConvertTz.java | 72 ++++++++-
.../expressions/functions/scalar/FromUnixtime.java | 56 ++++++-
.../org/apache/doris/nereids/util/DateUtils.java | 15 ++
.../nereids/rules/rewrite/PartitionPrunerTest.java | 1 -
.../functions/scalar/ConvertTzTest.java | 173 +++++++++++++++++++++
.../functions/scalar/FromUnixtimeTest.java | 99 ++++++++++++
.../partition_prune/test_convert_tz.out | 5 +
.../month_quarter_cast_in_prune.groovy | 8 +-
.../partition_prune/test_convert_tz.groovy | 24 ++-
9 files changed, 441 insertions(+), 12 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
index 6a95bf0b149..195ea273af9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTz.java
@@ -18,12 +18,16 @@
package org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.common.DdlException;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.Expression;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.Monotonic;
import
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
@@ -31,10 +35,15 @@ import
org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.nereids.util.DateUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.util.List;
/**
@@ -90,7 +99,55 @@ public class ConvertTz extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- return child(1).isConstant() && child(2).isConstant();
+ if (!(child(1) instanceof StringLikeLiteral) || !(child(2) instanceof
StringLikeLiteral)) {
+ return false;
+ }
+ ZoneId fromZone = parseZoneId((StringLikeLiteral) child(1));
+ ZoneId toZone = parseZoneId((StringLikeLiteral) child(2));
+ if (fromZone == null || toZone == null) {
+ return false;
+ }
+ if (toZone.getRules().isFixedOffset()) {
+ return true;
+ }
+ if (lower == null || upper == null) {
+ return false;
+ }
+ LocalDateTime lowerDateTime = toLocalDateTime(lower);
+ LocalDateTime upperDateTime = toLocalDateTime(upper);
+ if (lowerDateTime == null || upperDateTime == null) {
+ return false;
+ }
+ if (lowerDateTime.equals(upperDateTime)) {
+ return true;
+ }
+ if (upperDateTime.isBefore(lowerDateTime)) {
+ return false;
+ }
+ /*
+ * convert_tz can be treated as a composition of two mappings:
+ *
+ * source local time x -> instant by from_tz -> target local time by
to_tz.
+ *
+ * After PR #64029, the first mapping is monotonic non-decreasing. A
spring gap in from_tz
+ * flattens skipped local times to the transition instant, and a
fall-back overlap uses the
+ * pre-transition offset before jumping forward at the overlap end.
Neither case makes the
+ * instant move backward as x increases.
+ *
+ * The second mapping, instant -> to_tz local time, is also monotonic
non-decreasing except
+ * at a to_tz fall-back transition, where the displayed local time
jumps backward. Therefore
+ * convert_tz is non-monotonic for a partition only when the instant
interval obtained from
+ * interpreting the partition bounds in from_tz crosses a to_tz
fall-back transition instant.
+ *
+ * Partition pruning folds both endpoints before deriving the function
range, so a fall-back
+ * instant inside (fromInstant(lower), fromInstant(upper)] disables
the monotonic shortcut.
+ */
+ Instant lowerInstant =
DateTimeLiteral.convertLocalToInstant(lowerDateTime, fromZone);
+ Instant upperInstant =
DateTimeLiteral.convertLocalToInstant(upperDateTime, fromZone);
+ if (upperInstant.isBefore(lowerInstant)) {
+ return false;
+ }
+ return !DateUtils.hasFallbackTransitionInInstantRange(toZone,
lowerInstant, upperInstant);
}
@Override
@@ -107,4 +164,17 @@ public class ConvertTz extends ScalarFunction
public Expression withConstantArgs(Expression literal) {
return new ConvertTz(literal, child(1), child(2));
}
+
+ private ZoneId parseZoneId(StringLikeLiteral timeZone) {
+ try {
+ String standardizedTimeZone =
TimeUtils.checkTimeZoneValidAndStandardize(timeZone.getStringValue());
+ return ZoneId.of(standardizedTimeZone, TimeUtils.timeZoneAliasMap);
+ } catch (DdlException | DateTimeException e) {
+ return null;
+ }
+ }
+
+ private LocalDateTime toLocalDateTime(Literal literal) {
+ return literal instanceof DateLiteral ? ((DateLiteral)
literal).toJavaDateType() : null;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtime.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtime.java
index 7492de7a243..64d571654fe 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtime.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtime.java
@@ -24,6 +24,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.Monotonic;
import
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.NumericLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.BigIntType;
@@ -35,6 +36,11 @@ import org.apache.doris.nereids.util.DateUtils;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.ZoneId;
import java.util.List;
/**
@@ -109,15 +115,27 @@ public class FromUnixtime extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (1 == arity()) {
+ if (arity() == 2 && !isMonotonicFormat()) {
+ return false;
+ }
+ ZoneId timeZone;
+ try {
+ timeZone = DateUtils.getTimeZone();
+ } catch (DateTimeException e) {
+ return false;
+ }
+ if (timeZone.getRules().isFixedOffset()) {
return true;
}
- Expression format = child(1);
- if (!(format instanceof StringLikeLiteral)) {
+ if (lower == null || upper == null) {
return false;
}
- String str = ((StringLikeLiteral) format).getValue();
- return DateUtils.monoFormat.contains(str);
+ Instant lowerInstant = toInstant(lower);
+ Instant upperInstant = toInstant(upper);
+ if (lowerInstant == null || upperInstant == null ||
upperInstant.isBefore(lowerInstant)) {
+ return false;
+ }
+ return !DateUtils.hasFallbackTransitionInInstantRange(timeZone,
lowerInstant, upperInstant);
}
@Override
@@ -137,4 +155,32 @@ public class FromUnixtime extends ScalarFunction
}
return new FromUnixtime(literal, child(1));
}
+
+ private boolean isMonotonicFormat() {
+ Expression format = child(1);
+ if (!(format instanceof StringLikeLiteral)) {
+ return false;
+ }
+ return DateUtils.monoFormat.contains(((StringLikeLiteral)
format).getValue());
+ }
+
+ private Instant toInstant(Literal literal) {
+ if (!(literal instanceof NumericLiteral)) {
+ return null;
+ }
+ BigDecimal value = ((NumericLiteral) literal).getBigDecimalValue();
+ if (value.signum() < 0) {
+ return null;
+ }
+ try {
+ long seconds = value.setScale(0,
RoundingMode.DOWN).longValueExact();
+ long nanos = value.subtract(BigDecimal.valueOf(seconds))
+ .movePointRight(9)
+ .setScale(0, RoundingMode.DOWN)
+ .longValueExact();
+ return Instant.ofEpochSecond(seconds, nanos);
+ } catch (ArithmeticException | DateTimeException e) {
+ return null;
+ }
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
index 9ac8ff9c1db..e339471e447 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/DateUtils.java
@@ -24,6 +24,7 @@ import org.apache.doris.qe.ConnectContext;
import com.google.common.collect.ImmutableSet;
import java.time.DayOfWeek;
+import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
@@ -35,6 +36,7 @@ import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.WeekFields;
+import java.time.zone.ZoneOffsetTransition;
import java.util.Locale;
import java.util.Set;
@@ -406,4 +408,17 @@ public class DateUtils {
}
return
ZoneId.of(ConnectContext.get().getSessionVariable().getTimeZone());
}
+
+ /**Determine whether there is a fallback transition within the interval
(lower, upper].
+ * @return If there is one, return true.*/
+ public static boolean hasFallbackTransitionInInstantRange(ZoneId zoneId,
Instant lower, Instant upper) {
+ ZoneOffsetTransition transition =
zoneId.getRules().nextTransition(lower);
+ while (transition != null && !transition.getInstant().isAfter(upper)) {
+ if (transition.isOverlap()) {
+ return true;
+ }
+ transition =
zoneId.getRules().nextTransition(transition.getInstant());
+ }
+ return false;
+ }
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
index f4f78da1e30..3fe07a49f9c 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
@@ -354,4 +354,3 @@ public class PartitionPrunerTest extends TestWithFeService {
return new ListPartitionItem(partitionKeys.build());
}
}
-
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTzTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTzTest.java
new file mode 100644
index 00000000000..f280d831a50
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ConvertTzTest.java
@@ -0,0 +1,173 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.types.DateTimeType;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class ConvertTzTest {
+ private final SlotReference timestampSlot = new SlotReference("ts",
DateTimeType.INSTANCE);
+
+ @Test
+ void testIsMonotonicWithoutDstTransition() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-01-10 00:00:00"),
+ new DateTimeLiteral("2021-01-11 00:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithFixedOffsetTargetZoneAndUnboundedSourceRange() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ null,
+ new DateTimeLiteral("2021-03-28 02:00:00")));
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 02:00:00"),
+ null));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtUpperBoundaryInTargetZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertFalse(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 00:00:00"),
+ new DateTimeLiteral("2021-10-31 01:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtLowerBoundaryInTargetZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 01:00:00"),
+ new DateTimeLiteral("2021-10-31 02:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtLowerBoundaryInTargetZone2() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 01:00:00"),
+ new DateTimeLiteral("2021-10-31 01:30:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtBoundaryInTargetZone3() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 01:30:00"),
+ new DateTimeLiteral("2021-10-31 02:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtBoundaryInTargetZone4() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-30 23:00:00"),
+ new DateTimeLiteral("2021-10-31 00:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackAtBoundaryInTargetZone5() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertFalse(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 00:00:00"),
+ new DateTimeLiteral("2021-10-31 02:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstSpringGapInTargetZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("UTC"), new VarcharLiteral("Europe/Paris"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-03-28 00:00:00"),
+ new DateTimeLiteral("2021-03-28 02:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstGapInSourceZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-03-28 02:00:00"),
+ new DateTimeLiteral("2021-03-28 03:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstGapAtLowerBoundaryInSourceZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-03-28 01:00:00"),
+ new DateTimeLiteral("2021-03-28 02:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstGapAtUpperBoundaryInSourceZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-03-28 03:00:00"),
+ new DateTimeLiteral("2021-03-28 04:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstGapInSourceZoneOverlap() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-03-28 02:00:00"),
+ new DateTimeLiteral("2021-03-28 04:00:00")));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackInSourceZone() {
+ ConvertTz convertTz = new ConvertTz(timestampSlot,
+ new VarcharLiteral("Europe/Paris"), new VarcharLiteral("UTC"));
+
+ Assertions.assertTrue(convertTz.isMonotonic(
+ new DateTimeLiteral("2021-10-31 01:00:00"),
+ new DateTimeLiteral("2021-10-31 03:00:00")));
+ }
+}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtimeTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtimeTest.java
new file mode 100644
index 00000000000..9c3aabab979
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromUnixtimeTest.java
@@ -0,0 +1,99 @@
+// 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.doris.nereids.trees.expressions.functions.scalar;
+
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.types.BigIntType;
+import org.apache.doris.qe.ConnectContext;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class FromUnixtimeTest {
+ private final SlotReference timestampSlot = new SlotReference("ts",
BigIntType.INSTANCE);
+ private ConnectContext previousContext;
+
+ @BeforeEach
+ void setUp() {
+ previousContext = ConnectContext.get();
+ ConnectContext connectContext = new ConnectContext();
+ connectContext.setThreadLocalInfo();
+ }
+
+ @AfterEach
+ void tearDown() {
+ ConnectContext.remove();
+ if (previousContext != null) {
+ previousContext.setThreadLocalInfo();
+ }
+ }
+
+ @Test
+ void testIsMonotonicWithFixedOffsetTimeZone() {
+ setTimeZone("+00:00");
+ FromUnixtime fromUnixtime = new FromUnixtime(timestampSlot);
+
+ Assertions.assertTrue(fromUnixtime.isMonotonic(
+ new BigIntLiteral(1635638400L), new
BigIntLiteral(1635642000L)));
+ }
+
+ @Test
+ void testIsMonotonicWithoutDstTransition() {
+ setTimeZone("Europe/Paris");
+ FromUnixtime fromUnixtime = new FromUnixtime(timestampSlot);
+
+ Assertions.assertTrue(fromUnixtime.isMonotonic(
+ new BigIntLiteral(1610236800L), new
BigIntLiteral(1610323200L)));
+ }
+
+ @Test
+ void testIsMonotonicWithDstFallbackTransition() {
+ setTimeZone("Europe/Paris");
+ FromUnixtime fromUnixtime = new FromUnixtime(timestampSlot);
+
+ Assertions.assertFalse(fromUnixtime.isMonotonic(
+ new BigIntLiteral(1635638400L), new
BigIntLiteral(1635642000L)));
+ }
+
+ @Test
+ void testIsMonotonicWithFormatAndDstFallbackTransition() {
+ setTimeZone("Europe/Paris");
+ FromUnixtime fromUnixtime = new FromUnixtime(timestampSlot,
+ new VarcharLiteral("%Y-%m-%d %H:%i:%s"));
+
+ Assertions.assertFalse(fromUnixtime.isMonotonic(
+ new BigIntLiteral(1635638400L), new
BigIntLiteral(1635642000L)));
+ }
+
+ @Test
+ void testIsMonotonicWithNonMonotonicFormat() {
+ setTimeZone("+00:00");
+ FromUnixtime fromUnixtime = new FromUnixtime(timestampSlot, new
VarcharLiteral("%W"));
+
+ Assertions.assertFalse(fromUnixtime.isMonotonic(
+ new BigIntLiteral(1610236800L), new
BigIntLiteral(1610323200L)));
+ }
+
+ private void setTimeZone(String timeZone) {
+ ConnectContext.get().getSessionVariable().setTimeZone(timeZone);
+ }
+}
diff --git
a/regression-test/data/nereids_rules_p0/partition_prune/test_convert_tz.out
b/regression-test/data/nereids_rules_p0/partition_prune/test_convert_tz.out
new file mode 100644
index 00000000000..8d5a0dcb23c
--- /dev/null
+++ b/regression-test/data/nereids_rules_p0/partition_prune/test_convert_tz.out
@@ -0,0 +1,5 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !convert_tz_dst --
+2021-10-31T00:30
+2021-10-31T01:30
+
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/month_quarter_cast_in_prune.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/month_quarter_cast_in_prune.groovy
index 88235c9ab81..be89d056e94 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/month_quarter_cast_in_prune.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/month_quarter_cast_in_prune.groovy
@@ -199,20 +199,20 @@ suite("month_quarter_cast_in_prune") {
explain {
sql """select * from from_unixtime_t where from_unixtime(a,"%Y-%m-%d
%T") <'2001-05-16 16:00:00'"""
- contains("partitions=2/5 (p1,p2)")
+ contains("partitions=3/5 (p1,p2,p5)")
}
explain {
sql """select * from from_unixtime_t where from_unixtime(a,"%Y-%m-%d
%T") <='2001-05-16 16:00:00'"""
- contains("partitions=3/5 (p1,p2,p3)")
+ contains("partitions=4/5 (p1,p2,p3,p5)")
}
explain {
sql """select * from from_unixtime_t where from_unixtime(a,"yyyyMMdd")
< '20330518'"""
- contains("partitions=3/5 (p1,p2,p3)")
+ contains("partitions=4/5 (p1,p2,p3,p5)")
}
explain {
sql """select * from from_unixtime_t where from_unixtime(a,"yyyyMMdd")
> '20330518'"""
- contains("partitions=3/5 (p1,p4,p5)")
+ contains("partitions=4/5 (p1,p2,p4,p5)")
}
explain {
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/test_convert_tz.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/test_convert_tz.groovy
index a6c62d0b0eb..fa271806e8c 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/test_convert_tz.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/test_convert_tz.groovy
@@ -95,4 +95,26 @@ suite("test_convert_tz") {
contains("partitions=3/3 (p1,p2,p3)")
}
}
-}
\ No newline at end of file
+
+ sql "drop table if exists test_convert_tz_dst;"
+ sql """CREATE TABLE test_convert_tz_dst
+ (
+ ts DATETIME NOT NULL
+ )
+ ENGINE = olap
+ PARTITION BY range (ts)
+ (
+ PARTITION `p_00` VALUES [('2021-10-31 00:00:00'), ('2021-10-31
01:00:00')),
+ PARTITION `p_01` VALUES [('2021-10-31 01:00:00'), ('2021-10-31
02:00:00')),
+ PARTITION `p_02` VALUES [('2021-10-31 02:00:00'), ('2021-10-31
03:00:00'))
+ ) DISTRIBUTED BY HASH (ts)
+ PROPERTIES(
+ "storage_format" = "DEFAULT",
+ "replication_num" = "1");"""
+ sql "insert into test_convert_tz_dst values('2021-10-31
00:30:00'),('2021-10-31 01:30:00'),('2021-10-31 02:30:00')"
+ explain {
+ sql "SELECT * FROM test_convert_tz_dst WHERE convert_tz(ts, 'UTC',
'Europe/Paris') = '2021-10-31 02:30:00';"
+ contains("partitions=2/3 (p_00,p_01)")
+ }
+ order_qt_convert_tz_dst "SELECT * FROM test_convert_tz_dst WHERE
convert_tz(ts, 'UTC', 'Europe/Paris') = '2021-10-31 02:30:00';"
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]