This is an automated email from the ASF dual-hosted git repository.
Mryange 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 81556a1b5ef [fix](function) Keep date partition pruning correct across
time zones (#68681)
81556a1b5ef is described below
commit 81556a1b5ef5d52412a8380e505238bc744f46a8
Author: Mryange <[email protected]>
AuthorDate: Thu Oct 8 11:17:56 2026 +0800
[fix](function) Keep date partition pruning correct across time zones
(#68681)
Nereids derives partition ranges from monotonic date functions. Root
cause: converting epoch values to local time can move backward at a
historical time zone fallback, but the planner previously treated these
functions as increasing across that transition and could prune
partitions containing matching rows. This PR centralizes date
monotonicity checks, including the checks used by casts and date
functions, and evaluates time zone transitions within each bounded
partition range. Ranges that cross a relevant transition remain
unpruned, while safe ranges can still be pruned. It also makes FE
constant folding use Doris session time zone aliases: with `CST`, the
planner previously recognized a safe range as monotonic but failed to
fold its partition endpoints, so it could not prune that range.
Regression coverage checks both historical fallback ranges and actual
pruning of a safe `CST` range.
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [ ] Regression test
- [ ] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
https://github.com/apache/doris-website/pull/1214 -->
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
---
.../doris/nereids/trees/expressions/Cast.java | 110 +-------
.../expressions/functions/DateAddSubMonotonic.java | 2 +-
.../functions/DateCeilFloorMonotonic.java | 11 +-
.../expressions/functions/DateDiffMonotonic.java | 3 +-
.../expressions/functions/FromSecondMonotonic.java | 9 +-
.../expressions/functions/MonotonicityUtils.java | 296 +++++++++++++++++++++
.../expressions/functions/scalar/ConvertTz.java | 7 +-
.../expressions/functions/scalar/DateFormat.java | 10 +-
.../expressions/functions/scalar/DayOfMonth.java | 8 +-
.../expressions/functions/scalar/DayOfYear.java | 7 +-
.../functions/scalar/FromMicrosecond.java | 6 +
.../functions/scalar/FromMillisecond.java | 6 +
.../expressions/functions/scalar/FromSecond.java | 6 +
.../expressions/functions/scalar/FromUnixtime.java | 12 +-
.../trees/expressions/functions/scalar/Hour.java | 9 +-
.../expressions/functions/scalar/Microsecond.java | 27 +-
.../trees/expressions/functions/scalar/Minute.java | 23 +-
.../trees/expressions/functions/scalar/Month.java | 7 +-
.../expressions/functions/scalar/Nanosecond.java | 26 +-
.../expressions/functions/scalar/Quarter.java | 7 +-
.../trees/expressions/functions/scalar/Second.java | 25 +-
.../expressions/functions/scalar/TimeFormat.java | 10 +-
.../functions/scalar/UnixTimestamp.java | 7 +-
.../expressions/functions/scalar/YearWeek.java | 6 +-
.../org/apache/doris/nereids/util/DateUtils.java | 33 +--
.../rules/rewrite/PruneOlapScanPartitionTest.java | 19 ++
.../functions/scalar/FromSecondMonotonicTest.java | 98 +++++++
.../month_quarter_cast_in_prune.groovy | 70 +++--
28 files changed, 521 insertions(+), 339 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
index e2316b40a60..2c9795e1b44 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Cast.java
@@ -17,20 +17,15 @@
package org.apache.doris.nereids.trees.expressions;
-import org.apache.doris.common.util.TimeUtils;
-import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.exceptions.UnboundException;
import org.apache.doris.nereids.trees.expressions.functions.Monotonic;
+import org.apache.doris.nereids.trees.expressions.functions.MonotonicityUtils;
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.TimeStampNsLiteral;
-import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.BigIntType;
import org.apache.doris.nereids.types.DataType;
-import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DecimalV2Type;
import org.apache.doris.nereids.types.DecimalV3Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -40,19 +35,11 @@ import org.apache.doris.nereids.types.SmallIntType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.TimeStampNsType;
-import org.apache.doris.nereids.types.TimeStampTzType;
import org.apache.doris.nereids.types.TinyIntType;
-import org.apache.doris.nereids.types.coercion.DateLikeType;
-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.time.ZoneOffset;
import java.util.List;
import java.util.Objects;
@@ -410,99 +397,6 @@ public class Cast extends Expression implements
UnaryExpression, Monotonic {
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- DataType childType = child().getDataType();
- if (!(childType instanceof DateLikeType && targetType instanceof
DateLikeType)) {
- return false;
- }
-
- if (targetType instanceof TimeStampNsType &&
!isRangeWithinTimeStampNs(lower, upper)) {
- return false;
- }
-
- if (childType instanceof TimeStampTzType
- && (targetType instanceof DateTimeV2Type || targetType
instanceof TimeStampNsType)) {
- int destinationScale = targetType instanceof DateTimeV2Type
- ? ((DateTimeV2Type) targetType).getScale() :
TimeStampNsType.SCALE;
- return isTimeStampTzToLocalDateTimeMonotonic(
- (TimeStampTzType) childType, destinationScale, lower,
upper);
- }
- if (childType instanceof TimeStampNsType && targetType instanceof
TimeStampTzType) {
- return isTimeStampNsToTimeStampTzMonotonic(
- (TimeStampTzType) targetType, lower, upper);
- }
- return true;
- }
-
- private boolean isRangeWithinTimeStampNs(Literal lower, Literal upper) {
- if (lower == null || upper == null) {
- return false;
- }
- try {
- return !(lower.checkedCastTo(targetType) instanceof NullLiteral)
- && !(upper.checkedCastTo(targetType) instanceof
NullLiteral);
- } catch (AnalysisException e) {
- return false;
- }
- }
-
- private boolean isTimeStampTzToLocalDateTimeMonotonic(
- TimeStampTzType sourceType, int destinationScale, Literal lower,
Literal upper) {
- ZoneId timeZone;
- try {
- timeZone = TimeUtils.getDorisZoneId();
- } catch (DateTimeException e) {
- return false;
- }
- if (timeZone.getRules().isFixedOffset()) {
- return true;
- }
- // Scale reduction rounds the UTC value before applying the session
timezone. That rounding
- // can move values across a fall-back transition just outside the
original partition range.
- if (destinationScale < sourceType.getScale()) {
- return false;
- }
- if (!(lower instanceof TimestampTzLiteral) || !(upper instanceof
TimestampTzLiteral)) {
- return false;
- }
-
- // TimestampTzLiteral stores UTC civil fields. The cast renders those
instants in the
- // session timezone, which moves backward at a fall-back transition.
- Instant lowerInstant = ((TimestampTzLiteral)
lower).toJavaDateType().toInstant(ZoneOffset.UTC);
- Instant upperInstant = ((TimestampTzLiteral)
upper).toJavaDateType().toInstant(ZoneOffset.UTC);
- if (upperInstant.isBefore(lowerInstant)) {
- return false;
- }
- return !DateUtils.hasFallbackTransitionInInstantRange(timeZone,
lowerInstant, upperInstant);
- }
-
- private boolean isTimeStampNsToTimeStampTzMonotonic(
- TimeStampTzType destinationType, Literal lower, Literal upper) {
- ZoneId timeZone;
- try {
- timeZone = TimeUtils.getDorisZoneId();
- } catch (DateTimeException e) {
- return false;
- }
- if (timeZone.getRules().isFixedOffset()) {
- return true;
- }
- if (!(lower instanceof TimeStampNsLiteral) || !(upper instanceof
TimeStampNsLiteral)) {
- return false;
- }
- LocalDateTime lowerDateTime = roundTimeStampNs(
- (TimeStampNsLiteral) lower, destinationType.getScale());
- LocalDateTime upperDateTime = roundTimeStampNs(
- (TimeStampNsLiteral) upper, destinationType.getScale());
- if (upperDateTime.isBefore(lowerDateTime)) {
- return false;
- }
- return !DateUtils.hasGapTransitionInLocalDateTimeRange(
- timeZone, lowerDateTime, upperDateTime);
- }
-
- private LocalDateTime roundTimeStampNs(TimeStampNsLiteral literal, int
scale) {
- long factor = (long) Math.pow(10, DateUtils.NANOSECOND_SCALE - scale);
- LocalDateTime dateTime = literal.toJavaDateType().plusNanos(factor /
2);
- return dateTime.withNano((int) (dateTime.getNano() / factor * factor));
+ return
MonotonicityUtils.isDateLikeCastMonotonic(child().getDataType(), targetType,
lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateAddSubMonotonic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateAddSubMonotonic.java
index 7fec22fd9d3..96265850456 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateAddSubMonotonic.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateAddSubMonotonic.java
@@ -23,7 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.literal.Literal;
public interface DateAddSubMonotonic extends Monotonic {
@Override
default boolean isMonotonic(Literal lower, Literal upper) {
- return child(1) instanceof Literal;
+ return MonotonicityUtils.hasConstantOtherArguments(this, 0);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateCeilFloorMonotonic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateCeilFloorMonotonic.java
index 5abdb2bb62f..836f889abc1 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateCeilFloorMonotonic.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateCeilFloorMonotonic.java
@@ -30,16 +30,7 @@ import java.util.Optional;
public interface DateCeilFloorMonotonic extends RoundingMonotonic {
@Override
default boolean isMonotonic(Literal lower, Literal upper) {
- switch (arity()) {
- case 1:
- return true;
- case 2:
- return !(child(0) instanceof Literal) && child(1) instanceof
Literal;
- case 3:
- return !(child(0) instanceof Literal) && child(1) instanceof
Literal && child(2) instanceof Literal;
- default:
- return false;
- }
+ return MonotonicityUtils.hasConstantRoundingArguments(this);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateDiffMonotonic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateDiffMonotonic.java
index daaea895b6d..1fb17f2b2dd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateDiffMonotonic.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateDiffMonotonic.java
@@ -23,8 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.literal.Literal;
public interface DateDiffMonotonic extends Monotonic {
@Override
default boolean isMonotonic(Literal lower, Literal upper) {
- return !(child(0) instanceof Literal) && child(1) instanceof Literal
- || child(0) instanceof Literal && !(child(1) instanceof
Literal);
+ return MonotonicityUtils.hasOneVariableArgument(this);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FromSecondMonotonic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FromSecondMonotonic.java
index 96dfa7ac650..6df932c9577 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FromSecondMonotonic.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/FromSecondMonotonic.java
@@ -17,17 +17,16 @@
package org.apache.doris.nereids.trees.expressions.functions;
-import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
/** monotonicity for from_{xx}second */
public interface FromSecondMonotonic extends Monotonic {
+ /** Number of input units in one epoch second. */
+ long getEpochUnitsPerSecond();
+
@Override
default boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof BigIntLiteral) {
- return ((BigIntLiteral) lower).getValue() >= 0;
- }
- return false;
+ return MonotonicityUtils.isEpochToLocalMonotonic(lower, upper,
getEpochUnitsPerSecond());
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/MonotonicityUtils.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/MonotonicityUtils.java
new file mode 100644
index 00000000000..7efcce3a102
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/MonotonicityUtils.java
@@ -0,0 +1,296 @@
+// 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;
+
+import org.apache.doris.common.util.TimeUtils;
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+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;
+import org.apache.doris.nereids.trees.expressions.literal.TimeStampNsLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.TimeStampNsType;
+import org.apache.doris.nereids.types.TimeStampTzType;
+import org.apache.doris.nereids.types.coercion.DateLikeType;
+import org.apache.doris.nereids.util.DateUtils;
+
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
+
+/** Shared range predicates for monotonic date and time expressions. */
+public final class MonotonicityUtils {
+ // The last second accepted by FE constant folding for
from_{second,millisecond,microsecond}.
+ private static final long MAX_EPOCH_SECONDS = 253402271999L;
+
+ private MonotonicityUtils() {
+ }
+
+ /** All arguments other than the range argument must be literals. */
+ public static boolean hasConstantOtherArguments(ExpressionTrait
expression, int rangeArgument) {
+ for (int i = 0; i < expression.arity(); i++) {
+ if (i != rangeArgument && !(expression.child(i) instanceof
Literal)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean hasOneVariableArgument(ExpressionTrait expression) {
+ return expression.child(0) instanceof Literal ^ expression.child(1)
instanceof Literal;
+ }
+
+ public static boolean hasConstantRoundingArguments(ExpressionTrait
expression) {
+ return expression.arity() >= 1 && expression.arity() <= 3
+ && (expression.arity() == 1 || !(expression.child(0)
instanceof Literal))
+ && hasConstantOtherArguments(expression, 0);
+ }
+
+ public static boolean hasMonotonicFormat(Expression format) {
+ return format instanceof StringLikeLiteral
+ && DateUtils.monoFormat.contains(((StringLikeLiteral)
format).getValue());
+ }
+
+ /** A calendar field cannot wrap while its enclosing date fields are
unchanged. */
+ public static boolean isWithinSameYear(Literal lower, Literal upper) {
+ return hasSameDatePrefix(lower, upper, 1);
+ }
+
+ public static boolean isWithinSameMonth(Literal lower, Literal upper) {
+ return hasSameDatePrefix(lower, upper, 2);
+ }
+
+ public static boolean isWithinSameDay(Literal lower, Literal upper) {
+ return hasSameDatePrefix(lower, upper, 3);
+ }
+
+ private static boolean hasSameDatePrefix(Literal lower, Literal upper, int
fields) {
+ if (!(lower instanceof DateLiteral && upper instanceof DateLiteral)) {
+ return false;
+ }
+ DateLiteral start = (DateLiteral) lower;
+ DateLiteral end = (DateLiteral) upper;
+ return start.getYear() == end.getYear()
+ && (fields < 2 || start.getMonth() == end.getMonth())
+ && (fields < 3 || start.getDay() == end.getDay());
+ }
+
+ /** Preserve the date-only behavior of sub-day extractors. */
+ public static boolean isWithinSameHour(Literal lower, Literal upper) {
+ return hasSameTimePrefix(lower, upper, 1);
+ }
+
+ public static boolean isWithinSameMinute(Literal lower, Literal upper) {
+ return hasSameTimePrefix(lower, upper, 2);
+ }
+
+ public static boolean isWithinSameSecond(Literal lower, Literal upper) {
+ return hasSameTimePrefix(lower, upper, 3);
+ }
+
+ private static boolean hasSameTimePrefix(Literal lower, Literal upper, int
fields) {
+ if (lower instanceof TimeStampNsLiteral && upper instanceof
TimeStampNsLiteral) {
+ TimeStampNsLiteral start = (TimeStampNsLiteral) lower;
+ TimeStampNsLiteral end = (TimeStampNsLiteral) upper;
+ return hasSameDatePrefix(lower, upper, 3)
+ && start.getHour() == end.getHour()
+ && (fields < 2 || start.getMinute() == end.getMinute())
+ && (fields < 3 || start.getSecond() == end.getSecond());
+ }
+ if (lower instanceof DateTimeLiteral && upper instanceof
DateTimeLiteral) {
+ DateTimeLiteral start = (DateTimeLiteral) lower;
+ DateTimeLiteral end = (DateTimeLiteral) upper;
+ return hasSameDatePrefix(lower, upper, 3)
+ && start.getHour() == end.getHour()
+ && (fields < 2 || start.getMinute() == end.getMinute())
+ && (fields < 3 || start.getSecond() == end.getSecond());
+ }
+ return lower instanceof DateLiteral && upper instanceof DateLiteral;
+ }
+
+ /** Check the range in the unit used by from_second, from_millisecond, or
from_microsecond. */
+ public static boolean isEpochToLocalMonotonic(Literal lower, Literal
upper, long unitsPerSecond) {
+ if (!(lower instanceof BigIntLiteral) || ((BigIntLiteral)
lower).getValue() < 0) {
+ return false;
+ }
+ ZoneId zoneId;
+ try {
+ zoneId = TimeUtils.getDorisZoneId();
+ } catch (DateTimeException e) {
+ return false;
+ }
+ if (zoneId.getRules().isFixedOffset()) {
+ return true;
+ }
+ if (!(upper instanceof BigIntLiteral)) {
+ return false;
+ }
+ long lowerValue = ((BigIntLiteral) lower).getValue();
+ long upperValue = ((BigIntLiteral) upper).getValue();
+ if (upperValue < lowerValue || upperValue / unitsPerSecond >
MAX_EPOCH_SECONDS) {
+ return false;
+ }
+ Instant lowerInstant = epochToInstant(lowerValue, unitsPerSecond);
+ Instant upperInstant = epochToInstant(upperValue, unitsPerSecond);
+ if (LocalDateTime.ofInstant(upperInstant, zoneId).getYear() > 9999) {
+ return false;
+ }
+ return isInstantToLocalMonotonic(zoneId, lowerInstant, upperInstant);
+ }
+
+ private static Instant epochToInstant(long value, long unitsPerSecond) {
+ return Instant.ofEpochSecond(value / unitsPerSecond,
+ value % unitsPerSecond * (1000000000L / unitsPerSecond));
+ }
+
+ /** Instant-to-local conversion can move backward only at a fall-back
transition. */
+ public static boolean isInstantToLocalMonotonic(ZoneId zoneId, Instant
lower, Instant upper) {
+ return zoneId.getRules().isFixedOffset()
+ || lower != null && upper != null && !upper.isBefore(lower)
+ && !hasFallbackTransitionInInstantRange(zoneId, lower, upper);
+ }
+
+ /** Local-to-instant conversion may lose its order across a spring-forward
gap. */
+ public static boolean isLocalToInstantMonotonic(ZoneId zoneId,
LocalDateTime lower, LocalDateTime upper) {
+ return zoneId.getRules().isFixedOffset()
+ || lower != null && upper != null && !upper.isBefore(lower)
+ && !hasGapTransitionInLocalDateTimeRange(zoneId, lower, upper);
+ }
+
+ /** Whether the instant interval (lower, upper] crosses a fall-back
transition. */
+ private static boolean hasFallbackTransitionInInstantRange(ZoneId zoneId,
Instant lower, Instant upper) {
+ ZoneRules rules = zoneId.getRules();
+ ZoneOffsetTransition transition = rules.nextTransition(lower);
+ while (transition != null && !transition.getInstant().isAfter(upper)) {
+ if (transition.isOverlap()) {
+ return true;
+ }
+ transition = rules.nextTransition(transition.getInstant());
+ }
+ return false;
+ }
+
+ /** Whether the local interval intersects a spring-forward gap. */
+ private static boolean hasGapTransitionInLocalDateTimeRange(
+ ZoneId zoneId, LocalDateTime lower, LocalDateTime upper) {
+ ZoneRules rules = zoneId.getRules();
+ Instant searchStart = lower.minusDays(2).atZone(zoneId).toInstant();
+ ZoneOffsetTransition transition = rules.nextTransition(searchStart);
+ while (transition != null &&
!transition.getDateTimeBefore().isAfter(upper)) {
+ if (transition.isGap()
+ && upper.isAfter(transition.getDateTimeBefore())
+ && lower.isBefore(transition.getDateTimeAfter())) {
+ return true;
+ }
+ transition = rules.nextTransition(transition.getInstant());
+ }
+ return false;
+ }
+
+ /** Dispatch date-like casts to their range checks. */
+ public static boolean isDateLikeCastMonotonic(
+ DataType sourceType, DataType targetType, Literal lower, Literal
upper) {
+ if (!(sourceType instanceof DateLikeType && targetType instanceof
DateLikeType)) {
+ return false;
+ }
+ if (targetType instanceof TimeStampNsType &&
!isRangeWithinTimeStampNs(targetType, lower, upper)) {
+ return false;
+ }
+ if (sourceType instanceof TimeStampTzType
+ && (targetType instanceof DateTimeV2Type || targetType
instanceof TimeStampNsType)) {
+ int destinationScale = targetType instanceof DateTimeV2Type
+ ? ((DateTimeV2Type) targetType).getScale() :
TimeStampNsType.SCALE;
+ return isTimeStampTzToLocalMonotonic(
+ (TimeStampTzType) sourceType, destinationScale, lower,
upper);
+ }
+ if (sourceType instanceof TimeStampNsType && targetType instanceof
TimeStampTzType) {
+ return isTimeStampNsToTimeStampTzMonotonic((TimeStampTzType)
targetType, lower, upper);
+ }
+ return true;
+ }
+
+ private static boolean isRangeWithinTimeStampNs(DataType targetType,
Literal lower, Literal upper) {
+ if (lower == null || upper == null) {
+ return false;
+ }
+ try {
+ return !(lower.checkedCastTo(targetType) instanceof NullLiteral)
+ && !(upper.checkedCastTo(targetType) instanceof
NullLiteral);
+ } catch (AnalysisException e) {
+ return false;
+ }
+ }
+
+ /** Check a TIMESTAMPTZ cast to a local datetime after accounting for its
output scale. */
+ public static boolean isTimeStampTzToLocalMonotonic(
+ TimeStampTzType sourceType, int destinationScale, Literal lower,
Literal upper) {
+ ZoneId zoneId;
+ try {
+ zoneId = TimeUtils.getDorisZoneId();
+ } catch (DateTimeException e) {
+ return false;
+ }
+ if (zoneId.getRules().isFixedOffset()) {
+ return true;
+ }
+ // Rounding UTC before zone conversion may cross a fall-back just
outside the input range.
+ if (destinationScale < sourceType.getScale()
+ || !(lower instanceof TimestampTzLiteral) || !(upper
instanceof TimestampTzLiteral)) {
+ return false;
+ }
+ Instant lowerInstant = ((TimestampTzLiteral)
lower).toJavaDateType().toInstant(ZoneOffset.UTC);
+ Instant upperInstant = ((TimestampTzLiteral)
upper).toJavaDateType().toInstant(ZoneOffset.UTC);
+ return isInstantToLocalMonotonic(zoneId, lowerInstant, upperInstant);
+ }
+
+ /** Check a local TIMESTAMP_NS cast after rounding to the TIMESTAMPTZ
output scale. */
+ public static boolean isTimeStampNsToTimeStampTzMonotonic(
+ TimeStampTzType destinationType, Literal lower, Literal upper) {
+ ZoneId zoneId;
+ try {
+ zoneId = TimeUtils.getDorisZoneId();
+ } catch (DateTimeException e) {
+ return false;
+ }
+ if (zoneId.getRules().isFixedOffset()) {
+ return true;
+ }
+ if (!(lower instanceof TimeStampNsLiteral) || !(upper instanceof
TimeStampNsLiteral)) {
+ return false;
+ }
+ LocalDateTime lowerDateTime = roundTimeStampNs((TimeStampNsLiteral)
lower, destinationType.getScale());
+ LocalDateTime upperDateTime = roundTimeStampNs((TimeStampNsLiteral)
upper, destinationType.getScale());
+ return isLocalToInstantMonotonic(zoneId, lowerDateTime, upperDateTime);
+ }
+
+ private static LocalDateTime roundTimeStampNs(TimeStampNsLiteral literal,
int scale) {
+ long factor = (long) Math.pow(10, DateUtils.NANOSECOND_SCALE - scale);
+ LocalDateTime dateTime = literal.toJavaDateType().plusNanos(factor /
2);
+ return dateTime.withNano((int) (dateTime.getNano() / factor * factor));
+ }
+}
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 5606b023987..317f5bb1894 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
@@ -24,6 +24,7 @@ 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.MonotonicityUtils;
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;
@@ -36,7 +37,6 @@ import
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.TimeStampNsType;
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;
@@ -147,10 +147,7 @@ public class ConvertTz extends ScalarFunction
*/
Instant lowerInstant =
DateTimeLiteral.convertLocalToInstant(lowerDateTime, fromZone);
Instant upperInstant =
DateTimeLiteral.convertLocalToInstant(upperDateTime, fromZone);
- if (upperInstant.isBefore(lowerInstant)) {
- return false;
- }
- return !DateUtils.hasFallbackTransitionInInstantRange(toZone,
lowerInstant, upperInstant);
+ return MonotonicityUtils.isInstantToLocalMonotonic(toZone,
lowerInstant, upperInstant);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateFormat.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateFormat.java
index 5a5884d75ab..809afe0e366 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateFormat.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateFormat.java
@@ -21,16 +21,15 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.TimeStampNsType;
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;
@@ -82,12 +81,7 @@ public class DateFormat extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- Expression format = child(1);
- if (!(format instanceof StringLikeLiteral)) {
- return false;
- }
- String str = ((StringLikeLiteral) format).getValue();
- return DateUtils.monoFormat.contains(str);
+ return MonotonicityUtils.hasMonotonicFormat(child(1));
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfMonth.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfMonth.java
index 07ef40c2814..0229c5a04ed 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfMonth.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfMonth.java
@@ -21,8 +21,8 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.Literal;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -95,10 +95,6 @@ public class DayOfMonth extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof DateLiteral && upper instanceof DateLiteral) {
- return ((DateLiteral) lower).getYear() == ((DateLiteral)
upper).getYear()
- && ((DateLiteral) lower).getMonth() == ((DateLiteral)
upper).getMonth();
- }
- return false;
+ return MonotonicityUtils.isWithinSameMonth(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfYear.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfYear.java
index 917e2cf95c9..a9687d0db1d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfYear.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayOfYear.java
@@ -21,8 +21,8 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.Literal;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -95,9 +95,6 @@ public class DayOfYear extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof DateLiteral && upper instanceof DateLiteral) {
- return ((DateLiteral) lower).getYear() == ((DateLiteral)
upper).getYear();
- }
- return false;
+ return MonotonicityUtils.isWithinSameYear(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMicrosecond.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMicrosecond.java
index a7c21daae0b..0247fe2e24a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMicrosecond.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMicrosecond.java
@@ -41,6 +41,7 @@ public class FromMicrosecond extends ScalarFunction
FromSecondMonotonic {
public static final int RESULT_SCALE = 6;
+
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.of(RESULT_SCALE)).args(BigIntType.INSTANCE));
@@ -69,6 +70,11 @@ public class FromMicrosecond extends ScalarFunction
return visitor.visitScalarFunction(this, context);
}
+ @Override
+ public long getEpochUnitsPerSecond() {
+ return 1000000;
+ }
+
@Override
public Expression withConstantArgs(Expression literal) {
return new FromMicrosecond(literal);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMillisecond.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMillisecond.java
index b2a719f17fa..3e5eee219cb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMillisecond.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromMillisecond.java
@@ -40,6 +40,7 @@ public class FromMillisecond extends ScalarFunction
implements BinaryExpression, ExplicitlyCastableSignature,
PropagateNullLiteral, PropagateNullable,
FromSecondMonotonic {
public static final int RESULT_SCALE = 3;
+
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.of(RESULT_SCALE)).args(BigIntType.INSTANCE));
@@ -68,6 +69,11 @@ public class FromMillisecond extends ScalarFunction
return visitor.visitScalarFunction(this, context);
}
+ @Override
+ public long getEpochUnitsPerSecond() {
+ return 1000;
+ }
+
@Override
public Expression withConstantArgs(Expression literal) {
return new FromMillisecond(literal);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecond.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecond.java
index bde4678aefe..026c125c12c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecond.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecond.java
@@ -41,6 +41,7 @@ public class FromSecond extends ScalarFunction
FromSecondMonotonic {
public static final int RESULT_SCALE = 0;
+
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.of(RESULT_SCALE)).args(BigIntType.INSTANCE));
@@ -69,6 +70,11 @@ public class FromSecond extends ScalarFunction
return visitor.visitScalarFunction(this, context);
}
+ @Override
+ public long getEpochUnitsPerSecond() {
+ return 1;
+ }
+
@Override
public Expression withConstantArgs(Expression literal) {
return new FromSecond(literal);
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 2c218c4342d..46323676aed 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
@@ -22,6 +22,7 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
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.MonotonicityUtils;
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;
@@ -157,10 +158,7 @@ public class FromUnixtime extends ScalarFunction
}
Instant lowerInstant = toInstant(lower);
Instant upperInstant = toInstant(upper);
- if (lowerInstant == null || upperInstant == null ||
upperInstant.isBefore(lowerInstant)) {
- return false;
- }
- return !DateUtils.hasFallbackTransitionInInstantRange(timeZone,
lowerInstant, upperInstant);
+ return MonotonicityUtils.isInstantToLocalMonotonic(timeZone,
lowerInstant, upperInstant);
}
@Override
@@ -182,11 +180,7 @@ public class FromUnixtime extends ScalarFunction
}
private boolean isMonotonicFormat() {
- Expression format = child(1);
- if (!(format instanceof StringLikeLiteral)) {
- return false;
- }
- return DateUtils.monoFormat.contains(((StringLikeLiteral)
format).getValue());
+ return MonotonicityUtils.hasMonotonicFormat(child(1));
}
private void checkFractionFormatSpecifiers() {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Hour.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Hour.java
index 664412c4571..0a2873d5f3e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Hour.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Hour.java
@@ -21,8 +21,8 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.Literal;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -98,11 +98,6 @@ public class Hour extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof DateLiteral && upper instanceof DateLiteral) {
- return ((DateLiteral) lower).getYear() == ((DateLiteral)
upper).getYear()
- && ((DateLiteral) lower).getMonth() == ((DateLiteral)
upper).getMonth()
- && ((DateLiteral) lower).getDay() == ((DateLiteral)
upper).getDay();
- }
- return false;
+ return MonotonicityUtils.isWithinSameDay(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Microsecond.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Microsecond.java
index 444838d9edd..b5bc6ee1c6f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Microsecond.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Microsecond.java
@@ -21,11 +21,9 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.TimeStampNsLiteral;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
@@ -98,27 +96,6 @@ public class Microsecond extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof TimeStampNsLiteral && upper instanceof
TimeStampNsLiteral) {
- TimeStampNsLiteral lowerDateTime = (TimeStampNsLiteral) lower;
- TimeStampNsLiteral upperDateTime = (TimeStampNsLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour()
- && lowerDateTime.getMinute() == upperDateTime.getMinute()
- && lowerDateTime.getSecond() == upperDateTime.getSecond();
- }
- if (lower instanceof DateTimeLiteral && upper instanceof
DateTimeLiteral) {
- DateTimeLiteral lowerDateTime = (DateTimeLiteral) lower;
- DateTimeLiteral upperDateTime = (DateTimeLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour()
- && lowerDateTime.getMinute() == upperDateTime.getMinute()
- && lowerDateTime.getSecond() == upperDateTime.getSecond();
- } else {
- return lower instanceof DateLiteral && upper instanceof
DateLiteral;
- }
+ return MonotonicityUtils.isWithinSameSecond(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Minute.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Minute.java
index 18454ea4b05..4e24e41553a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Minute.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Minute.java
@@ -21,11 +21,9 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.TimeStampNsLiteral;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
@@ -99,23 +97,6 @@ public class Minute extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof TimeStampNsLiteral && upper instanceof
TimeStampNsLiteral) {
- TimeStampNsLiteral lowerDateTime = (TimeStampNsLiteral) lower;
- TimeStampNsLiteral upperDateTime = (TimeStampNsLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour();
- }
- if (lower instanceof DateTimeLiteral && upper instanceof
DateTimeLiteral) {
- DateTimeLiteral lowerDateTime = (DateTimeLiteral) lower;
- DateTimeLiteral upperDateTime = (DateTimeLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour();
- } else {
- return lower instanceof DateLiteral && upper instanceof
DateLiteral;
- }
+ return MonotonicityUtils.isWithinSameHour(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Month.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Month.java
index 550d9c465ad..6517e63e4b0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Month.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Month.java
@@ -21,8 +21,8 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.Literal;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -96,9 +96,6 @@ public class Month extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof DateLiteral && upper instanceof DateLiteral) {
- return ((DateLiteral) lower).getYear() == ((DateLiteral)
upper).getYear();
- }
- return false;
+ return MonotonicityUtils.isWithinSameYear(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Nanosecond.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Nanosecond.java
index a730f41353b..b631f0cb643 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Nanosecond.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Nanosecond.java
@@ -21,11 +21,9 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.TimeStampNsLiteral;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
@@ -90,26 +88,6 @@ public class Nanosecond extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof TimeStampNsLiteral && upper instanceof
TimeStampNsLiteral) {
- TimeStampNsLiteral lowerTimestamp = (TimeStampNsLiteral) lower;
- TimeStampNsLiteral upperTimestamp = (TimeStampNsLiteral) upper;
- return lowerTimestamp.getYear() == upperTimestamp.getYear()
- && lowerTimestamp.getMonth() == upperTimestamp.getMonth()
- && lowerTimestamp.getDay() == upperTimestamp.getDay()
- && lowerTimestamp.getHour() == upperTimestamp.getHour()
- && lowerTimestamp.getMinute() == upperTimestamp.getMinute()
- && lowerTimestamp.getSecond() ==
upperTimestamp.getSecond();
- }
- if (lower instanceof DateTimeLiteral && upper instanceof
DateTimeLiteral) {
- DateTimeLiteral lowerDateTime = (DateTimeLiteral) lower;
- DateTimeLiteral upperDateTime = (DateTimeLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour()
- && lowerDateTime.getMinute() == upperDateTime.getMinute()
- && lowerDateTime.getSecond() == upperDateTime.getSecond();
- }
- return lower instanceof DateLiteral && upper instanceof DateLiteral;
+ return MonotonicityUtils.isWithinSameSecond(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quarter.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quarter.java
index 60cbe488b84..a38dae15ea9 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quarter.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Quarter.java
@@ -21,8 +21,8 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.Literal;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -95,9 +95,6 @@ public class Quarter extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof DateLiteral && upper instanceof DateLiteral) {
- return ((DateLiteral) lower).getYear() == ((DateLiteral)
upper).getYear();
- }
- return false;
+ return MonotonicityUtils.isWithinSameYear(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Second.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Second.java
index 9ddcf11f7ba..7bad0e30f62 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Second.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Second.java
@@ -21,11 +21,9 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.TimeStampNsLiteral;
import org.apache.doris.nereids.trees.expressions.shape.UnaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
@@ -99,25 +97,6 @@ public class Second extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (lower instanceof TimeStampNsLiteral && upper instanceof
TimeStampNsLiteral) {
- TimeStampNsLiteral lowerDateTime = (TimeStampNsLiteral) lower;
- TimeStampNsLiteral upperDateTime = (TimeStampNsLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour()
- && lowerDateTime.getMinute() == upperDateTime.getMinute();
- }
- if (lower instanceof DateTimeLiteral && upper instanceof
DateTimeLiteral) {
- DateTimeLiteral lowerDateTime = (DateTimeLiteral) lower;
- DateTimeLiteral upperDateTime = (DateTimeLiteral) upper;
- return lowerDateTime.getYear() == upperDateTime.getYear()
- && lowerDateTime.getMonth() == upperDateTime.getMonth()
- && lowerDateTime.getDay() == upperDateTime.getDay()
- && lowerDateTime.getHour() == upperDateTime.getHour()
- && lowerDateTime.getMinute() == upperDateTime.getMinute();
- } else {
- return lower instanceof DateLiteral && upper instanceof
DateLiteral;
- }
+ return MonotonicityUtils.isWithinSameMinute(lower, upper);
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeFormat.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeFormat.java
index 12b08ce1fac..2995fc17f8d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeFormat.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/TimeFormat.java
@@ -22,15 +22,14 @@ import
org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
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.MonotonicityUtils;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
-import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.TimeStampNsType;
import org.apache.doris.nereids.types.TimeV2Type;
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;
@@ -85,12 +84,7 @@ public class TimeFormat extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- Expression format = child(1);
- if (!(format instanceof StringLikeLiteral)) {
- return false;
- }
- String str = ((StringLikeLiteral) format).getValue();
- return DateUtils.monoFormat.contains(str);
+ return MonotonicityUtils.hasMonotonicFormat(child(1));
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
index 2b6b8fb1aa4..9a19e7ee8b2 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UnixTimestamp.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.DateTimeLiteral;
@@ -171,11 +172,7 @@ public class UnixTimestamp extends ScalarFunction
}
LocalDateTime lowerDateTime = toLocalDateTime(lower);
LocalDateTime upperDateTime = toLocalDateTime(upper);
- if (lowerDateTime == null || upperDateTime == null ||
upperDateTime.isBefore(lowerDateTime)) {
- return false;
- }
- return !DateUtils.hasGapTransitionInLocalDateTimeRange(
- timeZone, lowerDateTime, upperDateTime);
+ return MonotonicityUtils.isLocalToInstantMonotonic(timeZone,
lowerDateTime, upperDateTime);
}
private LocalDateTime toLocalDateTime(Literal literal) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearWeek.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearWeek.java
index 632dd951e94..c5b857f3021 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearWeek.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearWeek.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.FunctionSignature;
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.MonotonicityUtils;
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.visitor.ExpressionVisitor;
@@ -88,10 +89,7 @@ public class YearWeek extends ScalarFunction
@Override
public boolean isMonotonic(Literal lower, Literal upper) {
- if (arity() == 1) {
- return true;
- }
- return child(1) instanceof Literal;
+ return MonotonicityUtils.hasConstantOtherArguments(this, 0);
}
@Override
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 f34f67b2bc7..4110a055e81 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
@@ -17,6 +17,7 @@
package org.apache.doris.nereids.util;
+import org.apache.doris.common.util.TimeUtils;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
import org.apache.doris.qe.ConnectContext;
@@ -503,36 +504,6 @@ public class DateUtils {
if (ConnectContext.get() == null ||
ConnectContext.get().getSessionVariable() == null) {
return ZoneId.systemDefault();
}
- 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;
- }
-
- /** Determine whether the local interval intersects a spring-forward gap.
*/
- public static boolean hasGapTransitionInLocalDateTimeRange(
- ZoneId zoneId, LocalDateTime lower, LocalDateTime upper) {
- ZoneRules rules = zoneId.getRules();
- Instant searchStart = lower.minusDays(2).atZone(zoneId).toInstant();
- ZoneOffsetTransition transition = rules.nextTransition(searchStart);
- while (transition != null &&
!transition.getDateTimeBefore().isAfter(upper)) {
- if (transition.isGap()
- && upper.isAfter(transition.getDateTimeBefore())
- && lower.isBefore(transition.getDateTimeAfter())) {
- return true;
- }
- transition = rules.nextTransition(transition.getInstant());
- }
- return false;
+ return TimeUtils.getDorisZoneId();
}
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartitionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartitionTest.java
index a59c5134f30..9d9c62af572 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartitionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartitionTest.java
@@ -159,6 +159,25 @@ class PruneOlapScanPartitionTest extends TestWithFeService
implements MemoPatter
test("testOlapScanPartitionWithSingleColumnCase", "col1 >= 0 and col1
<= 5", 2);
}
+ @Test
+ void testEpochConversionWithTimeZoneAlias() throws Exception {
+ createTable("create table epoch_conversion_alias_prune ("
+ + "epoch_sec bigint not null) "
+ + "partition by range(epoch_sec) ("
+ + "partition p1 values[('1719792000'), ('1721001600')),"
+ + "partition p2 values[('1721001600'), ('1722470400'))"
+ + ") distributed by hash(epoch_sec) buckets 1 "
+ + "properties ('replication_num'='1')");
+
+ String previousTimeZone =
connectContext.getSessionVariable().getTimeZone();
+ try {
+ connectContext.getSessionVariable().setTimeZone("CST");
+ test("epoch_conversion_alias_prune", "from_second(epoch_sec) <
'2024-07-10 00:00:00'", 1);
+ } finally {
+ connectContext.getSessionVariable().setTimeZone(previousTimeZone);
+ }
+ }
+
@Test
void testOlapScanPartitionPruneWithTablet() throws Exception {
Database db =
Env.getCurrentInternalCatalog().getDbOrMetaException("test");
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecondMonotonicTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecondMonotonicTest.java
new file mode 100644
index 00000000000..153545e6eb7
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/FromSecondMonotonicTest.java
@@ -0,0 +1,98 @@
+// 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.functions.FromSecondMonotonic;
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
+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;
+
+import java.util.Arrays;
+import java.util.List;
+
+class FromSecondMonotonicTest {
+ private final SlotReference epochSlot = new SlotReference("epoch",
BigIntType.INSTANCE);
+ private ConnectContext previousContext;
+
+ @BeforeEach
+ void setUp() {
+ previousContext = ConnectContext.get();
+ new ConnectContext().setThreadLocalInfo();
+ }
+
+ @AfterEach
+ void tearDown() {
+ ConnectContext.remove();
+ if (previousContext != null) {
+ previousContext.setThreadLocalInfo();
+ }
+ }
+
+ @Test
+ void testTimeZoneFallbackWithinRangeDisablesMonotonicity() {
+ List<FromSecondMonotonic> functions = Arrays.asList(
+ new FromSecond(epochSlot), new FromMillisecond(epochSlot), new
FromMicrosecond(epochSlot));
+ long[] fallbackSeconds = {1730613600L, 684867600L};
+ List<String> timeZones = Arrays.asList("America/New_York",
"Asia/Shanghai");
+ for (int i = 0; i < timeZones.size(); i++) {
+
ConnectContext.get().getSessionVariable().setTimeZone(timeZones.get(i));
+ for (FromSecondMonotonic function : functions) {
+ long units = function.getEpochUnitsPerSecond();
+ Assertions.assertFalse(function.isMonotonic(
+ new BigIntLiteral((fallbackSeconds[i] - 1) * units),
+ new BigIntLiteral((fallbackSeconds[i] + 1) * units)));
+ Assertions.assertTrue(function.isMonotonic(
+ new BigIntLiteral(1719792000L * units),
+ new BigIntLiteral(1719795600L * units)));
+ Assertions.assertFalse(function.isMonotonic(new
BigIntLiteral(1719792000), null));
+ }
+ }
+ }
+
+ @Test
+ void testShanghaiHistoricalFallbackAndEpochUnits() {
+ ConnectContext.get().getSessionVariable().setTimeZone("Asia/Shanghai");
+ Assertions.assertFalse(new FromMillisecond(epochSlot).isMonotonic(
+ new BigIntLiteral(100000), new BigIntLiteral(1000000009999L)));
+ Assertions.assertTrue(new FromMicrosecond(epochSlot).isMonotonic(
+ new BigIntLiteral(100000), new BigIntLiteral(1000000009999L)));
+ Assertions.assertFalse(new FromSecond(epochSlot).isMonotonic(
+ new BigIntLiteral(253402272000L), new
BigIntLiteral(253402272001L)));
+ }
+
+ @Test
+ void testFixedOffsetPreservesMonotonicityForNonnegativeInput() {
+ List<FromSecondMonotonic> functions = Arrays.asList(
+ new FromSecond(epochSlot), new FromMillisecond(epochSlot), new
FromMicrosecond(epochSlot));
+ for (String timeZone : Arrays.asList("+00:00", "+08:00")) {
+ ConnectContext.get().getSessionVariable().setTimeZone(timeZone);
+ for (FromSecondMonotonic function : functions) {
+ Assertions.assertTrue(function.isMonotonic(new
BigIntLiteral(0), new BigIntLiteral(1)));
+ Assertions.assertTrue(function.isMonotonic(new
BigIntLiteral(0), null));
+ Assertions.assertFalse(function.isMonotonic(new
BigIntLiteral(-1), new BigIntLiteral(1)));
+ Assertions.assertFalse(function.isMonotonic(null, new
BigIntLiteral(1)));
+ }
+ }
+ }
+}
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 be89d056e94..3a77c478977 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
@@ -125,29 +125,55 @@ suite("month_quarter_cast_in_prune") {
) distributed by hash(a) properties("replication_num"="1");"""
sql """INSERT INTO monotonic_function_t
values(10000,'1979-01-01','1979-01-01','abc'),(100000009999,'2012-01-01','2012-01-01','abc'),(100000009999999,'2020-01-01','2020-01-01','abc'),(10000000099999999,'2045-01-01','2045-01-01','abc')"""
- explain {
- sql """select * from monotonic_function_t where from_second(a) <
'2001-09-09 12:33:19' """
- contains("partitions=4/4 (p1,p2,p3,p4)")
- }
- explain {
- sql """select * from monotonic_function_t where from_second(a) >
'2001-09-09 12:33:19' """
- contains("partitions=4/4 (p1,p2,p3,p4)")
- }
- explain {
- sql """select * from monotonic_function_t where from_millisecond(a) <
'2001-09-09 12:33:19' """
- contains("partitions=4/4 (p1,p2,p3,p4)")
- }
- explain {
- sql """select * from monotonic_function_t where from_millisecond(a) >
'2001-09-09 12:33:19' """
- contains("partitions=3/4 (p1,p3,p4)")
- }
- explain {
- sql """select * from monotonic_function_t where from_microsecond(a) <
'2000-09-09 12:33:19' """
- contains("partitions=3/4 (p1,p2,p3)")
+ def originalTimeZone = sql("select @@time_zone")[0][0]
+ try {
+ sql "set time_zone = 'Asia/Shanghai'"
+ explain {
+ sql """select * from monotonic_function_t where from_second(a) <
'2001-09-09 12:33:19' """
+ contains("partitions=4/4 (p1,p2,p3,p4)")
+ }
+ explain {
+ sql """select * from monotonic_function_t where from_second(a) >
'2001-09-09 12:33:19' """
+ contains("partitions=4/4 (p1,p2,p3,p4)")
+ }
+ explain {
+ sql """select * from monotonic_function_t where
from_millisecond(a) < '2001-09-09 12:33:19' """
+ contains("partitions=4/4 (p1,p2,p3,p4)")
+ }
+ explain {
+ sql """select * from monotonic_function_t where
from_millisecond(a) > '2001-09-09 12:33:19' """
+ // p2 spans the 1991 Asia/Shanghai fall-back, so it is not
monotonic.
+ contains("partitions=4/4 (p1,p2,p3,p4)")
+ }
+ explain {
+ sql """select * from monotonic_function_t where
from_microsecond(a) < '2000-09-09 12:33:19' """
+ contains("partitions=4/4 (p1,p2,p3,p4)")
+ }
+ explain {
+ sql """select * from monotonic_function_t where
from_microsecond(a) > '2002-09-09 12:33:19' """
+ // p2 is a safe 1970 interval; p3 spans the 1991 fall-back.
+ contains("partitions=3/4 (p1,p3,p4)")
+ }
+ } finally {
+ sql "set time_zone = '${originalTimeZone}'"
}
- explain {
- sql """select * from monotonic_function_t where from_microsecond(a) >
'2002-09-09 12:33:19' """
- contains("partitions=2/4 (p1,p4)")
+
+ sql "drop table if exists epoch_cst_prune"
+ sql """create table epoch_cst_prune (a bigint) duplicate key(a)
+ partition by range(a) (
+ partition p2000 values [('946684800'), ('978307200')),
+ partition p2001 values [('978307200'), ('1009843200')),
+ partition p2002 values [('1009843200'), ('1041379200'))
+ ) distributed by hash(a) properties('replication_num'='1');"""
+ sql "insert into epoch_cst_prune values (946684800), (978307200),
(1009843200)"
+ try {
+ sql "set time_zone = 'CST'"
+ explain {
+ sql "select * from epoch_cst_prune where from_second(a) >
'2001-06-01 00:00:00'"
+ contains("partitions=2/3 (p2001,p2002)")
+ }
+ } finally {
+ sql "set time_zone = '${originalTimeZone}'"
}
// test makedate
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]