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 955f72035a3 [opt](partition prunning) Infer bare-column predicates
from monotonic functions for partition pruning (#66288)
955f72035a3 is described below
commit 955f72035a3b8ae1c38856c145c1ab62b7117b95
Author: foxtail463 <[email protected]>
AuthorDate: Wed Aug 26 12:03:17 2026 +0800
[opt](partition prunning) Infer bare-column predicates from monotonic
functions for partition pruning (#66288)
Problem Summary:
Partition pruning can binary-search sorted partitions only for
bare-column comparisons.
Predicates such as date_trunc(dt, 'day'), year(dt), and substring(col,
1, n) hide the
partition column and may fall back to slower per-partition evaluation.
Solution:
Derive safe necessary bounds on the bare partition column and use them
only during
partition pruning, while preserving the original predicate and AND/OR
structure.
A dedicated rounding trait models FLOOR/CEIL semantics and validates
unsafe arguments
and casts. Compound interval syntax is bound through DATE_ADD/DATE_SUB,
but is not
inverse-rewritten because overflow and clamping could change query
semantics.
Follow-up work:
This PR keeps the safety-checked inference local to partition pruning. A
follow-up PR will reuse it in global expression optimization so OLAP
zone maps
and ORC/Parquet min/max pruning can also benefit, with broader pushdown
semantics validated separately.
Co-authored-by: yangtao555 <[email protected]>
---
.../rules/InferPredicateFromMonotonicFunction.java | 338 ++++++++++++++++
.../rules/expression/rules/PartitionPruner.java | 3 +
.../functions/CalendarCeilMonotonic.java | 43 ++
.../functions/DateCeilFloorMonotonic.java | 22 +-
...lFloorMonotonic.java => RoundingMonotonic.java} | 33 +-
.../trees/expressions/functions/scalar/Date.java | 11 +-
.../expressions/functions/scalar/DateTrunc.java | 18 +-
.../expressions/functions/scalar/DayCeil.java | 6 +
.../expressions/functions/scalar/DayFloor.java | 6 +
.../expressions/functions/scalar/HourCeil.java | 6 +
.../expressions/functions/scalar/HourFloor.java | 6 +
.../expressions/functions/scalar/MinuteCeil.java | 6 +
.../expressions/functions/scalar/MinuteFloor.java | 6 +
.../expressions/functions/scalar/MonthCeil.java | 10 +-
.../expressions/functions/scalar/MonthFloor.java | 6 +
.../expressions/functions/scalar/QuarterCeil.java | 10 +-
.../expressions/functions/scalar/QuarterFloor.java | 6 +
.../expressions/functions/scalar/SecondCeil.java | 6 +
.../expressions/functions/scalar/SecondFloor.java | 6 +
.../trees/expressions/functions/scalar/ToDate.java | 11 +-
.../expressions/functions/scalar/ToMonday.java | 10 +-
.../expressions/functions/scalar/WeekCeil.java | 23 +-
.../expressions/functions/scalar/WeekFloor.java | 23 +-
.../expressions/functions/scalar/YearCeil.java | 10 +-
.../expressions/functions/scalar/YearFloor.java | 6 +
.../InferPredicateFromMonotonicFunctionTest.java | 446 +++++++++++++++++++++
.../test_add_sub_diff_ceil_floor.out | 4 +
.../partition_prune/always_mono_func.groovy | 14 +-
.../multi_column_range_partition.groovy | 6 +-
.../partition_prune/one_col_range_partition.groovy | 4 +-
.../partition_prune/one_key_range_part_test.groovy | 8 +-
.../one_key_range_part_update_test.groovy | 4 +-
.../test_add_sub_diff_ceil_floor.groovy | 23 ++
33 files changed, 1081 insertions(+), 59 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunction.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunction.java
new file mode 100644
index 00000000000..598bd0a2064
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunction.java
@@ -0,0 +1,338 @@
+// 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.rules.expression.rules;
+
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Left;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Year;
+import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+import org.apache.doris.nereids.types.coercion.CharacterType;
+import org.apache.doris.nereids.util.ExpressionUtils;
+import org.apache.doris.nereids.util.TypeCoercionUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+// When a partition predicate wraps the partition column in a function
+// (e.g. date_trunc(dt, 'day') >= '2020-01-01'), this class derives an extra
predicate on the
+// bare column (dt >= '2020-01-01') and appends it, so partition pruning has a
range to work with.
+//
+// Why: the fast pruning path binary-searches a partition list sorted by the
partition column
+// (PartitionPredicateToRange). Binary search only understands "bare column op
literal" shapes that
+// map to a range; once the column is wrapped by date_trunc / year / substring
it cannot, and pruning
+// falls back to per-partition evaluation. The derived predicate is the key
that unlocks binary
+// search. It lives only inside pruning and is never written back as a runtime
filter, so it adds no
+// per-row cost. See the caller PartitionPruner.pruneInternal.
+// Like the existing monotonic partition evaluator, it may avoid evaluating
the wrapped function in
+// a partition proven irrelevant; runtime errors from rows in such a partition
are therefore not observable.
+//
+// input: date_trunc(dt, 'day') >= '2020-01-05' (date_trunc is a floor,
floor(x) <= x)
+// infer: floor(dt) >= '2020-01-05' => dt >= '2020-01-05'
+// output: date_trunc(dt, 'day') >= '2020-01-05' AND dt >= '2020-01-05'
+//
+// Every derived predicate is a necessary condition of the original, resting
only on the function's
+// monotonic/rounding property and never on data distribution, so appending it
never changes results.
+// Three kinds are supported:
+// - prefix: substring(col,1,n) >= 'abc' => col >= 'abc' (a prefix
never sorts after the whole string)
+// - rounding: floor(col) >= c => col >= c; ceil(col) <= c => col <= c
+// - year: year(col) op y => a boundary date or the half-open range
[y-01-01, (y+1)-01-01)
+// For each kind, EqualTo yields only a single-sided non-strict bound
(floor(col)=c gives col >= c only).
+//
+// from_unixtime and similar are excluded on purpose: they map a unix
timestamp to local time, so the
+// inverse depends on timezone shifts (DST) rather than a globally fixed
property, and no safe bare-column
+// bound can be derived.
+final class InferPredicateFromMonotonicFunction {
+ private InferPredicateFromMonotonicFunction() {
+ }
+
+ // Comparison: derive directly. AND/OR: recurse into children and keep the
boolean structure.
+ // NOT and other shapes are returned unchanged, since a necessary
condition cannot cross negation.
+ static Expression inferForPartitionPrune(Expression partitionPredicate) {
+ if (partitionPredicate instanceof ComparisonPredicate) {
+ return appendInferredPredicate((ComparisonPredicate)
partitionPredicate);
+ }
+ if (partitionPredicate instanceof And) {
+ return rewriteConjunction((And) partitionPredicate);
+ }
+ if (partitionPredicate instanceof Or) {
+ return rewriteDisjunction((Or) partitionPredicate);
+ }
+ return partitionPredicate;
+ }
+
+ // Append the derived bare-column predicate with AND; return the
comparison unchanged if nothing is derived.
+ private static Expression appendInferredPredicate(ComparisonPredicate
comparison) {
+ Optional<Expression> inferred = infer(comparison);
+ if (!inferred.isPresent()) {
+ return comparison;
+ }
+ // year EqualTo derives a lower AND upper bound; flatten so everything
joins one AND.
+ List<Expression> conjuncts = Lists.newArrayList(comparison);
+ conjuncts.addAll(ExpressionUtils.extractConjunction(inferred.get()));
+ return ExpressionUtils.and(conjuncts);
+ }
+
+ // Preserve every original conjunct and append only new inferred
predicates.
+ private static Expression rewriteConjunction(And predicate) {
+ List<Expression> conjuncts =
ExpressionUtils.extractConjunction(predicate);
+ Set<Expression> existing = Sets.newHashSet(conjuncts);
+ List<Expression> rewrittenConjuncts =
Lists.newArrayListWithCapacity(conjuncts.size());
+ List<Expression> inferredConjuncts = Lists.newArrayList();
+ for (Expression conjunct : conjuncts) {
+ if (!(conjunct instanceof ComparisonPredicate)) {
+ rewrittenConjuncts.add(inferForPartitionPrune(conjunct));
+ continue;
+ }
+ rewrittenConjuncts.add(conjunct);
+ Optional<Expression> inferred = infer((ComparisonPredicate)
conjunct);
+ if (!inferred.isPresent()) {
+ continue;
+ }
+ for (Expression inferredConjunct :
ExpressionUtils.extractConjunction(inferred.get())) {
+ if (existing.add(inferredConjunct)) {
+ inferredConjuncts.add(inferredConjunct);
+ }
+ }
+ }
+ rewrittenConjuncts.addAll(inferredConjuncts);
+ return predicate.withChildren(rewrittenConjuncts);
+ }
+
+ // Derive per branch and keep the OR: necessary conditions of different
branches must not be
+ // lifted out and ANDed together. PartitionPredicateToRange unions the
branch ranges.
+ private static Expression rewriteDisjunction(Or predicate) {
+ List<Expression> children = predicate.children();
+ List<Expression> rewrittenChildren =
Lists.newArrayListWithCapacity(children.size());
+ for (Expression child : children) {
+ Expression rewritten = inferForPartitionPrune(child);
+ rewrittenChildren.add(rewritten);
+ }
+ return predicate.withChildren(rewrittenChildren);
+ }
+
+ // Try the three inference kinds in order. Precondition: the right side
must be a literal.
+ private static Optional<Expression> infer(ComparisonPredicate comparison) {
+ if (!(comparison.right() instanceof Literal)) {
+ return Optional.empty();
+ }
+
+ return inferPrefixPredicate(comparison)
+ .or(() -> inferYearPredicate(comparison))
+ .or(() -> inferRoundingPredicate(comparison));
+ }
+
+ // Prefix inference: a prefix never sorts after the whole string
(prefix(s) <= s), so
+ // prefix(col) >= 'abc' => col >= 'abc'. >/>= keep the operator; = yields
only the lower bound;
+ // </<= do not hold (prefix(col) <= 'abc' allows col = 'abd', which is
larger).
+ private static Optional<Expression>
inferPrefixPredicate(ComparisonPredicate comparison) {
+ if (!(comparison.right() instanceof StringLikeLiteral)) {
+ return Optional.empty();
+ }
+ Optional<Expression> source = prefixSource(comparison.left());
+ if (!source.isPresent()) {
+ return Optional.empty();
+ }
+
+ ComparisonPredicate inferred;
+ if (comparison instanceof GreaterThan || comparison instanceof
GreaterThanEqual) {
+ inferred = (ComparisonPredicate)
comparison.withChildren(source.get(), comparison.right());
+ } else if (comparison instanceof EqualTo) {
+ inferred = new GreaterThanEqual(source.get(), comparison.right());
+ } else {
+ return Optional.empty();
+ }
+ return Optional.of(inferredPredicate(inferred));
+ }
+
+ // Extract the bare column from a fixed-length, start-anchored prefix over
a character Slot:
+ // substring(col, 1, n) (position 1, length present) or left(col, n).
Otherwise empty.
+ private static Optional<Expression> prefixSource(Expression expression) {
+ Expression source;
+ Expression length;
+ if (expression instanceof Substring) {
+ Substring substring = (Substring) expression;
+ if (!(substring.getPosition() instanceof IntegerLikeLiteral)
+ || ((IntegerLikeLiteral)
substring.getPosition()).getBigDecimalValue()
+ .compareTo(BigDecimal.ONE) != 0
+ || !substring.getLength().isPresent()) {
+ return Optional.empty();
+ }
+ source = substring.getSource();
+ length = substring.getLength().get();
+ } else if (expression instanceof Left) {
+ source = expression.child(0);
+ length = expression.child(1);
+ } else {
+ return Optional.empty();
+ }
+
+ // A clean fixed-length prefix requires a character Slot source and a
positive integer length.
+ if (!(source instanceof Slot) || !(source.getDataType() instanceof
CharacterType)
+ || !(length instanceof IntegerLikeLiteral)
+ || ((IntegerLikeLiteral) length).getBigDecimalValue().signum()
<= 0) {
+ return Optional.empty();
+ }
+ return Optional.of(source);
+ }
+
+ // Rounding inference over a bare date column. floor(x) <= x gives a lower
bound;
+ // ceil(x) >= x gives an upper bound. EqualTo yields only the single-sided
non-strict bound.
+ // floor(dt) >= '2020-01-05' => dt >= '2020-01-05'
+ // ceil(dt) <= '2020-01-05' => dt <= '2020-01-05'
+ private static Optional<Expression>
inferRoundingPredicate(ComparisonPredicate comparison) {
+ if (!(comparison.left() instanceof RoundingMonotonic)) {
+ return Optional.empty();
+ }
+ RoundingMonotonic function = (RoundingMonotonic) comparison.left();
+ // A rounding function may take several arguments (e.g. date_trunc(dt,
'day'));
+ // getMonotonicFunctionChildIndex points at the rounded input column.
+ Expression source =
function.child(function.getMonotonicFunctionChildIndex());
+ if (!function.isRoundingRelationGuaranteed()) {
+ return Optional.empty();
+ }
+
+ ComparisonPredicate inferred;
+ if (function.getRoundingType() ==
RoundingMonotonic.RoundingType.FLOOR) {
+ if (comparison instanceof GreaterThan || comparison instanceof
GreaterThanEqual) {
+ inferred = (ComparisonPredicate)
comparison.withChildren(source, comparison.right());
+ } else if (comparison instanceof EqualTo) {
+ inferred = new GreaterThanEqual(source, comparison.right());
+ } else {
+ // floor gives no upper bound: floor(dt) <= c allows dt beyond
c within the same bucket.
+ return Optional.empty();
+ }
+ } else {
+ if (comparison instanceof LessThan || comparison instanceof
LessThanEqual) {
+ inferred = (ComparisonPredicate)
comparison.withChildren(source, comparison.right());
+ } else if (comparison instanceof EqualTo) {
+ inferred = new LessThanEqual(source, comparison.right());
+ } else {
+ return Optional.empty();
+ }
+ }
+ return inferredDatePredicate(inferred);
+ }
+
+ // Year inference: map year(dt) op y to a range on dt, where start =
y-01-01, end = (y+1)-01-01,
+ // so year(dt) = y is the half-open range [start, end). Operator mapping
(y=2020 example):
+ // year(dt) >= 2020 -> dt >= 2020-01-01
+ // year(dt) > 2020 -> dt >= 2021-01-01 (after year y means not
before next year's start)
+ // year(dt) < 2020 -> dt < 2020-01-01
+ // year(dt) <= 2020 -> dt < 2021-01-01 (not after year y means
before next year's start)
+ // year(dt) = 2020 -> dt >= 2020-01-01 AND dt < 2021-01-01
+ private static Optional<Expression> inferYearPredicate(ComparisonPredicate
comparison) {
+ // year returns SmallInt, so comparing against a wider integer literal
wraps it in an integer
+ // cast (cast(year(dt) as INT)); such a widening cast is
value-preserving, so see through it.
+ Expression left = comparison.left();
+ if (left instanceof Cast && left.child(0) instanceof Year
+ && left.getDataType().isIntegerLikeType()
+ && left.child(0).getDataType().width() <=
left.getDataType().width()) {
+ left = left.child(0);
+ }
+ if (!(left instanceof Year) || !(comparison.right() instanceof
IntegerLikeLiteral)) {
+ return Optional.empty();
+ }
+ Expression source = left.child(0);
+ if (!isDateSlot(source)) {
+ return Optional.empty();
+ }
+
+ BigInteger year = ((IntegerLikeLiteral)
comparison.right()).getBigDecimalValue().toBigIntegerExact();
+ Optional<DateV2Literal> start = firstDayOfYear(year);
+ // y=9999 is the date upper bound and has no next year, so end is
absent and branches that
+ // depend on it (>, <=, and the upper side of =) cannot be derived.
+ Optional<DateV2Literal> end = year.compareTo(BigInteger.valueOf(9999))
< 0
+ ? firstDayOfYear(year.add(BigInteger.ONE)) : Optional.empty();
+ if (comparison instanceof GreaterThanEqual && start.isPresent()) {
+ return Optional.of(inferredPredicate(new GreaterThanEqual(source,
start.get())));
+ }
+ if (comparison instanceof GreaterThan && end.isPresent()) {
+ return Optional.of(inferredPredicate(new GreaterThanEqual(source,
end.get())));
+ }
+ if (comparison instanceof LessThan && start.isPresent()) {
+ return Optional.of(inferredPredicate(new LessThan(source,
start.get())));
+ }
+ if (comparison instanceof LessThanEqual && end.isPresent()) {
+ return Optional.of(inferredPredicate(new LessThan(source,
end.get())));
+ }
+ if (!(comparison instanceof EqualTo) || !start.isPresent()) {
+ return Optional.empty();
+ }
+
+ // EqualTo: lower bound dt >= start, plus upper bound dt < end when
end is present.
+ Expression lower = inferredPredicate(new GreaterThanEqual(source,
start.get()));
+ if (!end.isPresent()) {
+ return Optional.of(lower);
+ }
+ Expression upper = inferredPredicate(new LessThan(source, end.get()));
+ return Optional.of(ExpressionUtils.and(ImmutableList.of(lower,
upper)));
+ }
+
+ // Build the year-01-01 literal; empty when the year is outside the valid
[0, 9999] range.
+ private static Optional<DateV2Literal> firstDayOfYear(BigInteger year) {
+ return year.signum() >= 0 && year.compareTo(BigInteger.valueOf(9999))
<= 0
+ ? Optional.of(new DateV2Literal(year.intValueExact(), 1, 1))
+ : Optional.empty();
+ }
+
+ // A date column we can infer on: a Slot of date-like type, excluding
timezone-aware timestamptz
+ // (its comparison depends on the session timezone and is not a globally
fixed relation).
+ private static boolean isDateSlot(Expression expression) {
+ return expression instanceof Slot &&
expression.getDataType().isDateLikeType()
+ && !expression.getDataType().isTimeStampTzType();
+ }
+
+ // Type-coerce the derived predicate and mark it inferred, so downstream
keeps it inside pruning
+ // and never writes it back as a user filter.
+ private static Expression inferredPredicate(ComparisonPredicate predicate)
{
+ return
TypeCoercionUtils.processComparisonPredicate(predicate).withInferred(true);
+ }
+
+ // The date floor/ceil binder may promote a DATE slot to DATETIMEV2
because its hidden default
+ // origin is DATETIMEV2. Derive against that actual argument first, then
reuse the comparison
+ // simplifier to recover a bare date Slot without losing non-midnight
boundary semantics.
+ private static Optional<Expression>
inferredDatePredicate(ComparisonPredicate predicate) {
+ ComparisonPredicate coerced = (ComparisonPredicate)
TypeCoercionUtils.processComparisonPredicate(predicate);
+ Expression simplified = SimplifyComparisonPredicate.simplify(coerced);
+ return simplified instanceof ComparisonPredicate &&
isDateSlot(simplified.child(0))
+ ? Optional.of(simplified.withInferred(true))
+ : Optional.empty();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
index 6d866da9154..353d019b742 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
@@ -185,6 +185,9 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
partitionPredicate = PartitionPruneExpressionExtractor.extract(
partitionPredicate, ImmutableSet.copyOf(partitionSlots),
cascadesContext);
Expression originalPartitionPredicate = partitionPredicate;
+ // Keep inferred ranges local to pruning. They can unlock
sorted-partition binary search,
+ // but must not be written back as extra runtime filter conjuncts.
+ partitionPredicate =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(partitionPredicate);
partitionPredicate =
PredicateRewriteForPartitionPrune.rewrite(partitionPredicate, cascadesContext);
int expandThreshold = cascadesContext.getAndCacheSessionVariable(
"partitionPruningExpandThreshold",
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/CalendarCeilMonotonic.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/CalendarCeilMonotonic.java
new file mode 100644
index 00000000000..85224838205
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/CalendarCeilMonotonic.java
@@ -0,0 +1,43 @@
+// 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.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
+
+/**
+ * Monotonicity of year, quarter, and month ceil functions. Calendar
arithmetic can clamp the
+ * origin's day at month end, so custom origins do not universally guarantee
ceil(x) >= x. For
+ * partition pruning, only the canonical first-day origin is accepted.
+ */
+public interface CalendarCeilMonotonic extends DateCeilFloorMonotonic {
+ @Override
+ default boolean isRoundingRelationGuaranteed() {
+ if (!DateCeilFloorMonotonic.super.isRoundingRelationGuaranteed()) {
+ return false;
+ }
+ if (arity() == 1 || (arity() == 2 && getArgument(1) instanceof
IntegerLikeLiteral)) {
+ return true;
+ }
+ Expression origin = getArgument(arity() - 1);
+ return origin instanceof DateLiteral
+ && ((DateLiteral)
origin).compareTo(DateTimeV2Literal.USE_IN_FLOOR_CEIL) == 0;
+ }
+}
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 71ad80a3471..2885a205230 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
@@ -17,10 +17,12 @@
package org.apache.doris.nereids.trees.expressions.functions;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
-/** monotonicity of XX_CEIL and XX_FLOOR */
-public interface DateCeilFloorMonotonic extends Monotonic {
+/** Monotonicity and rounding relation of date/time ceil and floor functions.
*/
+public interface DateCeilFloorMonotonic extends RoundingMonotonic {
@Override
default boolean isMonotonic(Literal lower, Literal upper) {
switch (arity()) {
@@ -44,4 +46,20 @@ public interface DateCeilFloorMonotonic extends Monotonic {
default int getMonotonicFunctionChildIndex() {
return 0;
}
+
+ @Override
+ default boolean isRoundingRelationGuaranteed() {
+ if (arity() == 1) {
+ return true;
+ }
+ if (arity() == 2 && getArgument(1).getDataType().isDateLikeType()) {
+ return true;
+ }
+ return (arity() == 2 || arity() == 3) &&
isPositiveIntegerLiteral(getArgument(1));
+ }
+
+ private boolean isPositiveIntegerLiteral(Expression expression) {
+ return expression instanceof IntegerLikeLiteral
+ && ((IntegerLikeLiteral)
expression).getBigDecimalValue().signum() > 0;
+ }
}
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/RoundingMonotonic.java
similarity index 54%
copy from
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateCeilFloorMonotonic.java
copy to
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/RoundingMonotonic.java
index 71ad80a3471..eccdfa95ab4 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/RoundingMonotonic.java
@@ -17,31 +17,18 @@
package org.apache.doris.nereids.trees.expressions.functions;
-import org.apache.doris.nereids.trees.expressions.literal.Literal;
-
-/** monotonicity of XX_CEIL and XX_FLOOR */
-public interface DateCeilFloorMonotonic extends Monotonic {
- @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;
- }
+/** A monotonic function whose result is guaranteed to be on one side of its
input. */
+public interface RoundingMonotonic extends Monotonic {
+ /** Direction in which the function rounds its input. */
+ enum RoundingType {
+ FLOOR,
+ CEIL
}
- @Override
- default boolean isPositive() {
- return true;
- }
+ RoundingType getRoundingType();
- @Override
- default int getMonotonicFunctionChildIndex() {
- return 0;
+ /** Whether the current arguments preserve the declared relation between
the result and input. */
+ default boolean isRoundingRelationGuaranteed() {
+ return true;
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Date.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Date.java
index 4b058ade725..f3b5577c1ea 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Date.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Date.java
@@ -20,9 +20,10 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
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.PropagateNullLiteral;
import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic.RoundingType;
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;
@@ -37,7 +38,8 @@ import java.util.List;
* ScalarFunction 'date'. This class is generated by GenerateFunction.
*/
public class Date extends ScalarFunction
- implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, PropagateNullLiteral, Monotonic {
+ implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, PropagateNullLiteral,
+ RoundingMonotonic {
//TODO: eliminate this function for Date input
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
@@ -80,6 +82,11 @@ public class Date extends ScalarFunction
return true;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public int getMonotonicFunctionChildIndex() {
return 0;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
index 3e057f2ff23..f2e5aa7355b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
@@ -21,9 +21,10 @@ import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.CustomSignature;
-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.functions.RoundingMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import
org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker;
import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
@@ -42,7 +43,7 @@ import java.util.List;
* ScalarFunction 'date_trunc'. This class is generated by GenerateFunction.
*/
public class DateTrunc extends ScalarFunction
- implements BinaryExpression, PropagateNullLiteral, PropagateNullable,
Monotonic, CustomSignature {
+ implements BinaryExpression, PropagateNullLiteral, PropagateNullable,
RoundingMonotonic, CustomSignature {
private static final List<String> LEGAL_TIME_UNIT =
ImmutableList.of("year", "quarter", "month", "week", "day",
"hour", "minute", "second");
@@ -168,6 +169,19 @@ public class DateTrunc extends ScalarFunction
return true;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
+ @Override
+ public boolean isRoundingRelationGuaranteed() {
+ int dateArgumentIndex = getMonotonicFunctionChildIndex();
+ Expression unit = child(dateArgumentIndex == 0 ? 1 : 0);
+ return unit instanceof StringLikeLiteral
+ && LEGAL_TIME_UNIT.contains(((StringLikeLiteral)
unit).getStringValue().toLowerCase());
+ }
+
@Override
public int getMonotonicFunctionChildIndex() {
return getArgument(0).getDataType().isDateLikeType() ? 0 : 1;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayCeil.java
index 260d3d4037a..56af70ff3fd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayCeil.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -102,6 +103,11 @@ public class DayCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitDayCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayFloor.java
index dece77f6ed9..c2fe1f899a1 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DayFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -102,6 +103,11 @@ public class DayFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitDayFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourCeil.java
index 86d6cc0fc41..f7411630e8c 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourCeil.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -99,6 +100,11 @@ public class HourCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitHourCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourFloor.java
index e27c9aaa409..0efcf1cdba4 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/HourFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -99,6 +100,11 @@ public class HourFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitHourFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteCeil.java
index 8896553e609..2046d8c0c0a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteCeil.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -98,6 +99,11 @@ public class MinuteCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitMinuteCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteFloor.java
index ebcd13ee2b4..30156f4377e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MinuteFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -98,6 +99,11 @@ public class MinuteFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitMinuteFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthCeil.java
index 0dc10ad8ab7..853930e9d43 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthCeil.java
@@ -19,10 +19,11 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
-import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.CalendarCeilMonotonic;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -38,7 +39,7 @@ import java.util.List;
* ScalarFunction 'month_ceil'. This class is generated by GenerateFunction.
*/
public class MonthCeil extends ScalarFunction
- implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, DateCeilFloorMonotonic {
+ implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, CalendarCeilMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.WILDCARD).args(DateTimeV2Type.WILDCARD),
@@ -102,6 +103,11 @@ public class MonthCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitMonthCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthFloor.java
index 8fe2d8aa3b9..ecc9535f155 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MonthFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -102,6 +103,11 @@ public class MonthFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitMonthFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterCeil.java
index 28e19e85e1f..db5a1cbe32e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterCeil.java
@@ -19,10 +19,11 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
-import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.CalendarCeilMonotonic;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -38,7 +39,7 @@ import java.util.List;
* ScalarFunction 'quarter_ceil'. This class is generated by GenerateFunction.
*/
public class QuarterCeil extends ScalarFunction
- implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, DateCeilFloorMonotonic {
+ implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, CalendarCeilMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.WILDCARD).args(DateTimeV2Type.WILDCARD),
@@ -103,6 +104,11 @@ public class QuarterCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitQuarterCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterFloor.java
index 026aaef6f86..0a8b4d020c1 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/QuarterFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -103,6 +104,11 @@ public class QuarterFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitQuarterFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondCeil.java
index 6cd0f37470a..4c8ab90e072 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondCeil.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -98,6 +99,11 @@ public class SecondCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitSecondCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondFloor.java
index 0731e50cbc3..29ba55a0538 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SecondFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.IntegerType;
@@ -98,6 +99,11 @@ public class SecondFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitSecondFloor(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToDate.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToDate.java
index 2bc2014822a..7bdb4627dfc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToDate.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToDate.java
@@ -20,9 +20,10 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
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.PropagateNullLiteral;
import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic.RoundingType;
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;
@@ -37,7 +38,8 @@ import java.util.List;
* ScalarFunction 'to_date'. This class is generated by GenerateFunction.
*/
public class ToDate extends ScalarFunction
- implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, PropagateNullLiteral, Monotonic {
+ implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, PropagateNullLiteral,
+ RoundingMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateV2Type.INSTANCE).args(DateTimeV2Type.WILDCARD));
@@ -78,6 +80,11 @@ public class ToDate extends ScalarFunction
return true;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public int getMonotonicFunctionChildIndex() {
return 0;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToMonday.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToMonday.java
index 101b7b6f46d..dec6449d2e5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToMonday.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ToMonday.java
@@ -20,8 +20,9 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
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.PropagateNullable;
+import org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.RoundingMonotonic.RoundingType;
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;
@@ -36,7 +37,7 @@ import java.util.List;
* ScalarFunction 'to_monday'. This class is generated by GenerateFunction.
*/
public class ToMonday extends ScalarFunction
- implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, Monotonic {
+ implements UnaryExpression, ExplicitlyCastableSignature,
PropagateNullable, RoundingMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateV2Type.INSTANCE).args(DateV2Type.INSTANCE),
@@ -79,6 +80,11 @@ public class ToMonday extends ScalarFunction
return true;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public int getMonotonicFunctionChildIndex() {
return 0;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekCeil.java
index a88751ea1ff..59c221257c6 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekCeil.java
@@ -19,9 +19,11 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
+import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonotonic;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -37,7 +39,7 @@ import java.util.List;
* ScalarFunction 'week_ceil'. This class is generated by GenerateFunction.
*/
public class WeekCeil extends ScalarFunction
- implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable {
+ implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, DateCeilFloorMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.WILDCARD).args(DateTimeV2Type.WILDCARD),
@@ -101,8 +103,27 @@ public class WeekCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitWeekCeil(this, context);
}
+
+ @Override
+ public Expression withConstantArgs(Expression literal) {
+ switch (arity()) {
+ case 1:
+ return new WeekCeil(literal);
+ case 2:
+ return new WeekCeil(literal, child(1));
+ case 3:
+ return new WeekCeil(literal, child(1), child(2));
+ default:
+ throw new IllegalStateException("The function " + getName() +
" has invalid child number.");
+ }
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekFloor.java
index b2e0a916a82..64e46e6858d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WeekFloor.java
@@ -19,9 +19,11 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
+import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonotonic;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -37,7 +39,7 @@ import java.util.List;
* ScalarFunction 'week_floor'. This class is generated by GenerateFunction.
*/
public class WeekFloor extends ScalarFunction
- implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable {
+ implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, DateCeilFloorMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.WILDCARD).args(DateTimeV2Type.WILDCARD),
@@ -101,8 +103,27 @@ public class WeekFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitWeekFloor(this, context);
}
+
+ @Override
+ public Expression withConstantArgs(Expression literal) {
+ switch (arity()) {
+ case 1:
+ return new WeekFloor(literal);
+ case 2:
+ return new WeekFloor(literal, child(1));
+ case 3:
+ return new WeekFloor(literal, child(1), child(2));
+ default:
+ throw new IllegalStateException("The function " + getName() +
" has invalid child number.");
+ }
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearCeil.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearCeil.java
index 358227c3677..f62ee50d99e 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearCeil.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearCeil.java
@@ -19,10 +19,11 @@ package
org.apache.doris.nereids.trees.expressions.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
-import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonotonic;
+import
org.apache.doris.nereids.trees.expressions.functions.CalendarCeilMonotonic;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -38,7 +39,7 @@ import java.util.List;
* ScalarFunction 'year_ceil'. This class is generated by GenerateFunction.
*/
public class YearCeil extends ScalarFunction
- implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, DateCeilFloorMonotonic {
+ implements ExplicitlyCastableSignature, PropagateNullLiteral,
PropagateNullable, CalendarCeilMonotonic {
private static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(DateTimeV2Type.WILDCARD).args(DateTimeV2Type.WILDCARD),
@@ -102,6 +103,11 @@ public class YearCeil extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.CEIL;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitYearCeil(this, context);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearFloor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearFloor.java
index 7ba43fb29ff..380cb846d29 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearFloor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearFloor.java
@@ -23,6 +23,7 @@ import
org.apache.doris.nereids.trees.expressions.functions.DateCeilFloorMonoton
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
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.functions.RoundingMonotonic.RoundingType;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DateTimeV2Type;
import org.apache.doris.nereids.types.DateV2Type;
@@ -102,6 +103,11 @@ public class YearFloor extends ScalarFunction
return SIGNATURES;
}
+ @Override
+ public RoundingType getRoundingType() {
+ return RoundingType.FLOOR;
+ }
+
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitYearFloor(this, context);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunctionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunctionTest.java
new file mode 100644
index 00000000000..cdaed30b6cc
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rules/InferPredicateFromMonotonicFunctionTest.java
@@ -0,0 +1,446 @@
+// 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.rules.expression.rules;
+
+import org.apache.doris.nereids.rules.expression.ExpressionRewriteTestHelper;
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import
org.apache.doris.nereids.trees.expressions.functions.executable.TimeRoundSeries;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DateTrunc;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Left;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.MonthCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.QuarterCeil;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.ToDate;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.WeekFloor;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.Year;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.YearCeil;
+import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.TimestampTzLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.TimeStampTzType;
+import org.apache.doris.nereids.types.TinyIntType;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+
+class InferPredicateFromMonotonicFunctionTest extends
ExpressionRewriteTestHelper {
+ private final SlotReference stringSlot = new SlotReference("s",
StringType.INSTANCE, true);
+ private final SlotReference dateTimeSlot = new SlotReference(
+ "dt", DateTimeV2Type.SYSTEM_DEFAULT, true);
+ private final SlotReference nonPartitionDateTimeSlot = new SlotReference(
+ "non_partition_dt", DateTimeV2Type.SYSTEM_DEFAULT, true);
+
+ @Test
+ void inferOnlyAfterExtractingPartitionPredicate() {
+ EqualTo partitionPredicate = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2026));
+ EqualTo nonPartitionPredicate = new EqualTo(
+ new Year(nonPartitionDateTimeSlot), new IntegerLiteral(2026));
+ Expression filter = typeCoercion(ExpressionUtils.and(
+ ImmutableList.of(partitionPredicate, nonPartitionPredicate)));
+
+ Expression extracted = PartitionPruneExpressionExtractor.extract(
+ filter, ImmutableSet.of(dateTimeSlot), cascadesContext);
+ Expression inferred =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(extracted);
+
+ assertInferredBound(inferred, GreaterThanEqual.class, dateTimeSlot);
+ assertInferredBound(inferred, LessThan.class, dateTimeSlot);
+ Assertions.assertTrue(inferred.getInputSlots().contains(dateTimeSlot));
+
Assertions.assertFalse(inferred.getInputSlots().contains(nonPartitionDateTimeSlot));
+ }
+
+ @Test
+ void inferPrefixLowerBoundFromSqlSyntax() {
+ Map<String, Slot> slots = Maps.newHashMap();
+ Expression original =
typeCoercion(replaceUnboundSlot(PARSER.parseExpression(
+ "substring(VS, 1, 4) >= 'a-value-longer-than-four'"), slots));
+
+ Expression rewritten =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(original);
+
+ assertInferredBound(rewritten, GreaterThanEqual.class,
slots.get("VS"));
+ assertOriginalPreserved(rewritten, original);
+ }
+
+ @Test
+ void inferStrictPrefixLowerBound() {
+ GreaterThan original = new GreaterThan(
+ new Left(stringSlot, new IntegerLiteral(4)), new
VarcharLiteral("abcd"));
+
+ Expression rewritten = rewrite(original);
+
+ assertInferredBound(rewritten, GreaterThan.class, stringSlot);
+ }
+
+ @Test
+ void inferPrefixEqualityAsLowerBound() {
+ EqualTo original = new EqualTo(substring(stringSlot, 1, 4), new
VarcharLiteral("abcd"));
+
+ Expression rewritten = rewrite(original);
+
+ assertInferredBound(rewritten, GreaterThanEqual.class, stringSlot);
+ }
+
+ @Test
+ void doNotInferNonPrefixOrUpperBound() {
+ LessThan upperBound = new LessThan(
+ substring(stringSlot, 1, 4), new VarcharLiteral("abcd"));
+ GreaterThanEqual nonPrefix = new GreaterThanEqual(
+ substring(stringSlot, 2, 4), new VarcharLiteral("abcd"));
+
+ Assertions.assertEquals(typeCoercion(upperBound), rewrite(upperBound));
+ Assertions.assertEquals(typeCoercion(nonPrefix), rewrite(nonPrefix));
+ }
+
+ @Test
+ void inferDateTruncAndToDateLowerBounds() {
+ GreaterThanEqual dateTrunc = new GreaterThanEqual(
+ new DateTrunc(dateTimeSlot, new VarcharLiteral("month")),
+ new DateTimeV2Literal("2026-07-01 00:00:00"));
+ EqualTo toDate = new EqualTo(new ToDate(dateTimeSlot), new
VarcharLiteral("2026-07-28"));
+
+ assertInferredBound(rewrite(dateTrunc), GreaterThanEqual.class,
dateTimeSlot);
+ assertInferredBound(rewrite(toDate), GreaterThanEqual.class,
dateTimeSlot);
+ }
+
+ @Test
+ void inferFloorAndCeilBounds() {
+ GreaterThan floor = new GreaterThan(
+ new DayFloor(dateTimeSlot), new DateTimeV2Literal("2026-07-28
00:00:00"));
+ GreaterThanEqual floorWithOrigin = new GreaterThanEqual(
+ new DayFloor(dateTimeSlot, new IntegerLiteral(3),
+ new DateTimeV2Literal("2020-01-01 00:00:00")),
+ new DateTimeV2Literal("2026-07-28 00:00:00"));
+ LessThanEqual ceil = new LessThanEqual(
+ new DayCeil(dateTimeSlot), new DateTimeV2Literal("2026-07-28
00:00:00"));
+ EqualTo weekFloor = new EqualTo(
+ new WeekFloor(dateTimeSlot), new DateTimeV2Literal("2026-07-27
00:00:00"));
+
+ assertInferredBound(rewrite(floor), GreaterThan.class, dateTimeSlot);
+ assertInferredBound(rewrite(floorWithOrigin), GreaterThanEqual.class,
dateTimeSlot);
+ assertInferredBound(rewrite(ceil), LessThanEqual.class, dateTimeSlot);
+ assertInferredBound(rewrite(weekFloor), GreaterThanEqual.class,
dateTimeSlot);
+ }
+
+ @Test
+ void inferDateCeilBoundFromSqlSyntax() {
+ Map<String, Slot> slots = Maps.newHashMap();
+ Expression predicate =
typeCoercion(replaceUnboundSlot(PARSER.parseExpression(
+ "date_ceil(AA, interval 1 day) <= '2026-07-28 00:00:00'"),
slots));
+ Expression dateCeil = predicate.child(0);
+
+ Assertions.assertInstanceOf(DayCeil.class, dateCeil);
+ Assertions.assertEquals(3, dateCeil.arity());
+
assertInferredBound(InferPredicateFromMonotonicFunction.inferForPartitionPrune(predicate),
+ LessThanEqual.class, slots.get("AA"));
+ }
+
+ @Test
+ void inferCalendarCeilWithDefaultOriginFromSqlSyntax() {
+ Map<String, Slot> slots = Maps.newHashMap();
+ Expression predicate =
typeCoercion(replaceUnboundSlot(PARSER.parseExpression(
+ "date_ceil(AA, interval 1 month) <= '2026-07-28 00:00:00'"),
slots));
+ Expression dateCeil = predicate.child(0);
+
+ Assertions.assertInstanceOf(MonthCeil.class, dateCeil);
+ Assertions.assertEquals(3, dateCeil.arity());
+
assertInferredBound(InferPredicateFromMonotonicFunction.inferForPartitionPrune(predicate),
+ LessThanEqual.class, slots.get("AA"));
+ }
+
+ @Test
+ void inferDateV2BoundsFromSqlSyntax() {
+ Map<String, Slot> slots = Maps.newHashMap();
+ Expression ceilPredicate =
typeCoercion(replaceUnboundSlot(PARSER.parseExpression(
+ "date_ceil(CC, interval 1 day) < '2026-07-28 12:00:00'"),
slots));
+ Expression floorPredicate =
typeCoercion(replaceUnboundSlot(PARSER.parseExpression(
+ "date_floor(CC, interval 1 day) >= '2026-07-28 12:00:00'"),
slots));
+
+ Assertions.assertInstanceOf(Cast.class,
ceilPredicate.child(0).child(0));
+ Assertions.assertInstanceOf(Cast.class,
floorPredicate.child(0).child(0));
+
+ ComparisonPredicate ceilBound = findInferredBound(
+
InferPredicateFromMonotonicFunction.inferForPartitionPrune(ceilPredicate),
+ LessThan.class, slots.get("CC"));
+ ComparisonPredicate floorBound = findInferredBound(
+
InferPredicateFromMonotonicFunction.inferForPartitionPrune(floorPredicate),
+ GreaterThanEqual.class, slots.get("CC"));
+ DateV2Literal nextDay = new DateV2Literal("2026-07-29");
+ Assertions.assertEquals(nextDay, ceilBound.right());
+ Assertions.assertEquals(nextDay, floorBound.right());
+ }
+
+ @Test
+ void inferWithCustomOriginButOnlyPositiveLiteralPeriod() {
+ GreaterThanEqual zeroPeriod = new GreaterThanEqual(
+ new DayFloor(dateTimeSlot, new IntegerLiteral(0)),
+ new DateTimeV2Literal("2026-07-28 00:00:00"));
+ LessThanEqual zeroPeriodWithOrigin = new LessThanEqual(
+ new DayCeil(dateTimeSlot, new IntegerLiteral(0),
+ DateTimeV2Literal.USE_IN_FLOOR_CEIL),
+ new DateTimeV2Literal("2026-07-28 00:00:00"));
+ SlotReference periodSlot = new SlotReference("period",
IntegerType.INSTANCE, false);
+ LessThanEqual dynamicPeriod = new LessThanEqual(
+ new DayCeil(dateTimeSlot, periodSlot,
DateTimeV2Literal.USE_IN_FLOOR_CEIL),
+ new DateTimeV2Literal("2026-07-28 00:00:00"));
+ LessThanEqual withOrigin = new LessThanEqual(
+ new DayCeil(dateTimeSlot, new DateTimeV2Literal("2020-01-01
00:00:00")),
+ new DateTimeV2Literal("2026-07-28 00:00:00"));
+
+ Assertions.assertEquals(typeCoercion(zeroPeriod), rewrite(zeroPeriod));
+ Assertions.assertEquals(typeCoercion(zeroPeriodWithOrigin),
rewrite(zeroPeriodWithOrigin));
+ Assertions.assertEquals(typeCoercion(dynamicPeriod),
rewrite(dynamicPeriod));
+ assertInferredBound(rewrite(withOrigin), LessThanEqual.class,
dateTimeSlot);
+ }
+
+ @Test
+ void doNotInferCalendarCeilWithCustomOrigin() {
+ DateTimeV2Literal boundary = new DateTimeV2Literal("2021-02-28
00:00:00");
+ DateTimeV2Literal monthEndOrigin = new DateTimeV2Literal("2021-01-31
00:00:00");
+ Assertions.assertEquals(boundary, TimeRoundSeries.monthCeil(
+ new DateTimeV2Literal("2021-02-28 12:00:00"), new
IntegerLiteral(1), monthEndOrigin));
+
+ List<Expression> predicates = ImmutableList.of(
+ new LessThanEqual(new MonthCeil(dateTimeSlot, monthEndOrigin),
boundary),
+ new LessThanEqual(new MonthCeil(dateTimeSlot, new
IntegerLiteral(1), monthEndOrigin), boundary),
+ new LessThanEqual(new QuarterCeil(dateTimeSlot, new
IntegerLiteral(1),
+ new DateTimeV2Literal("2020-11-30 00:00:00")),
boundary),
+ new LessThanEqual(new YearCeil(dateTimeSlot, new
IntegerLiteral(1),
+ new DateTimeV2Literal("2020-02-29 00:00:00")),
boundary));
+
+ for (Expression predicate : predicates) {
+ Assertions.assertEquals(typeCoercion(predicate),
rewrite(predicate));
+ }
+ }
+
+ @Test
+ void doNotInferTimestampTzRoundingBound() {
+ SlotReference timestampTzSlot = new SlotReference(
+ "ts", TimeStampTzType.SYSTEM_DEFAULT, true);
+ LessThanEqual predicate = new LessThanEqual(
+ new DayCeil(timestampTzSlot), new
TimestampTzLiteral("2026-07-28 00:00:00+00:00"));
+
+ Assertions.assertEquals(typeCoercion(predicate), rewrite(predicate));
+ }
+
+ @Test
+ void inferExactYearRange() {
+ EqualTo original = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2026));
+
+ Expression rewritten = rewrite(original);
+
+ assertInferredBound(rewritten, GreaterThanEqual.class, dateTimeSlot);
+ assertInferredBound(rewritten, LessThan.class, dateTimeSlot);
+ Assertions.assertEquals(3,
ExpressionUtils.extractConjunction(rewritten).size());
+ }
+
+ @Test
+ void inferAllYearComparisonDirections() {
+ assertInferredBound(rewrite(new GreaterThan(new Year(dateTimeSlot),
new IntegerLiteral(2026))),
+ GreaterThanEqual.class, dateTimeSlot);
+ assertInferredBound(rewrite(new GreaterThanEqual(new
Year(dateTimeSlot), new IntegerLiteral(2026))),
+ GreaterThanEqual.class, dateTimeSlot);
+ assertInferredBound(rewrite(new LessThan(new Year(dateTimeSlot), new
IntegerLiteral(2026))),
+ LessThan.class, dateTimeSlot);
+ assertInferredBound(rewrite(new LessThanEqual(new Year(dateTimeSlot),
new IntegerLiteral(2026))),
+ LessThan.class, dateTimeSlot);
+ }
+
+ @Test
+ void inferYearAtDateDomainBounds() {
+ EqualTo yearZero = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(0));
+ Expression rewrittenYearZero = rewrite(yearZero);
+ assertInferredBound(rewrittenYearZero, GreaterThanEqual.class,
dateTimeSlot);
+ assertInferredBound(rewrittenYearZero, LessThan.class, dateTimeSlot);
+
+ EqualTo year9999 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(9999));
+ Expression rewrittenYear9999 = rewrite(year9999);
+ Expression expectedLowerBound = typeCoercion(
+ new GreaterThanEqual(dateTimeSlot, new DateV2Literal(9999, 1,
1))).withInferred(true);
+
Assertions.assertTrue(ExpressionUtils.extractConjunction(rewrittenYear9999).contains(expectedLowerBound));
+ Assertions.assertEquals(2,
ExpressionUtils.extractConjunction(rewrittenYear9999).size());
+
+ GreaterThan afterLastYear = new GreaterThan(new Year(dateTimeSlot),
new IntegerLiteral(9999));
+ LessThanEqual throughLastYear = new LessThanEqual(new
Year(dateTimeSlot), new IntegerLiteral(9999));
+ EqualTo outsideDateDomain = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(10000));
+ Assertions.assertEquals(typeCoercion(afterLastYear),
rewrite(afterLastYear));
+ Assertions.assertEquals(typeCoercion(throughLastYear),
rewrite(throughLastYear));
+ Assertions.assertEquals(typeCoercion(outsideDateDomain),
rewrite(outsideDateDomain));
+ }
+
+ @Test
+ void doNotInferThroughNarrowingYearCast() {
+ EqualTo predicate = new EqualTo(
+ new Cast(new Year(dateTimeSlot), TinyIntType.INSTANCE), new
IntegerLiteral(1));
+
+ Assertions.assertEquals(typeCoercion(predicate), rewrite(predicate));
+ }
+
+ @Test
+ void doNotTruncateLargeYearLiteral() {
+ LessThan predicate = new LessThan(new Year(dateTimeSlot),
+ new LargeIntLiteral(new BigInteger("18446744073709553642")));
+
+ Assertions.assertEquals(typeCoercion(predicate), rewrite(predicate));
+ }
+
+ @Test
+ void preserveOriginalConjuncts() {
+ GreaterThanEqual predicate = new GreaterThanEqual(
+ dateTimeSlot, new DateTimeV2Literal("2026-07-28 00:00:00"));
+ Expression duplicateConjunction = new And(ImmutableList.of(predicate,
predicate));
+
+ Expression rewritten = InferPredicateFromMonotonicFunction
+ .inferForPartitionPrune(duplicateConjunction);
+
+ Assertions.assertInstanceOf(And.class, rewritten);
+ Assertions.assertEquals(2, rewritten.children().size());
+ }
+
+ @Test
+ void preserveOriginalOrderAndAppendOnceInsideConjunction() {
+ GreaterThanEqual prefix = new GreaterThanEqual(
+ substring(stringSlot, 1, 4), new VarcharLiteral("abcd"));
+ GreaterThan dateFloor = new GreaterThan(
+ new DayFloor(dateTimeSlot), new DateTimeV2Literal("2026-07-28
00:00:00"));
+ Expression original = ExpressionUtils.and(ImmutableList.of(prefix,
dateFloor));
+
+ Expression analyzedOriginal = typeCoercion(original);
+ Expression once =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(analyzedOriginal);
+ Expression twice =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(once);
+ List<Expression> originalConjuncts =
ExpressionUtils.extractConjunction(analyzedOriginal);
+ List<Expression> rewrittenConjuncts =
ExpressionUtils.extractConjunction(once);
+
+ Assertions.assertEquals(once, twice);
+ Assertions.assertEquals(originalConjuncts,
+ rewrittenConjuncts.subList(0, originalConjuncts.size()));
+ Assertions.assertEquals(4, rewrittenConjuncts.size());
+ }
+
+ @Test
+ void doNotAppendBoundAlreadyPresent() {
+ DateTimeV2Literal boundary = new DateTimeV2Literal("2026-07-28
00:00:00");
+ GreaterThanEqual dateFloor = new GreaterThanEqual(new
DayFloor(dateTimeSlot), boundary);
+ GreaterThanEqual existingBound = new GreaterThanEqual(dateTimeSlot,
boundary);
+ Expression original =
typeCoercion(ExpressionUtils.and(ImmutableList.of(dateFloor, existingBound)));
+
+ Expression rewritten =
InferPredicateFromMonotonicFunction.inferForPartitionPrune(original);
+
+ Assertions.assertEquals(ExpressionUtils.extractConjunction(original),
+ ExpressionUtils.extractConjunction(rewritten));
+ }
+
+ @Test
+ void inferEachDisjunctionBranch() {
+ EqualTo year2025 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2025));
+ EqualTo year2026 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2026));
+
+ Expression rewritten = rewrite(new Or(year2025, year2026));
+ List<Expression> disjunctions =
ExpressionUtils.extractDisjunction(rewritten);
+
+ Assertions.assertEquals(2, disjunctions.size());
+ for (Expression disjunction : disjunctions) {
+ assertInferredBound(disjunction, GreaterThanEqual.class,
dateTimeSlot);
+ assertInferredBound(disjunction, LessThan.class, dateTimeSlot);
+ Assertions.assertEquals(3,
ExpressionUtils.extractConjunction(disjunction).size());
+ }
+ Assertions.assertEquals(rewritten,
+
InferPredicateFromMonotonicFunction.inferForPartitionPrune(rewritten));
+ }
+
+ @Test
+ void inferNestedConjunctionAndDisjunction() {
+ EqualTo year2024 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2024));
+ EqualTo year2025 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2025));
+ EqualTo year2026 = new EqualTo(new Year(dateTimeSlot), new
IntegerLiteral(2026));
+ GreaterThan dayFloor = new GreaterThan(
+ new DayFloor(dateTimeSlot), new DateTimeV2Literal("2026-07-28
00:00:00"));
+ Expression original = ExpressionUtils.and(ImmutableList.of(
+ year2024,
+ new Or(year2025,
ExpressionUtils.and(ImmutableList.of(year2026, dayFloor)))));
+
+ Expression rewritten = rewrite(original);
+ Or rewrittenOr = (Or)
ExpressionUtils.extractConjunction(rewritten).stream()
+ .filter(Or.class::isInstance)
+ .findFirst()
+ .orElseThrow(AssertionError::new);
+ List<Expression> disjunctions =
ExpressionUtils.extractDisjunction(rewrittenOr);
+
+ Assertions.assertEquals(2, disjunctions.size());
+ assertInferredBound(rewritten, GreaterThanEqual.class, dateTimeSlot);
+ assertInferredBound(disjunctions.get(0), GreaterThanEqual.class,
dateTimeSlot);
+ assertInferredBound(disjunctions.get(0), LessThan.class, dateTimeSlot);
+ assertInferredBound(disjunctions.get(1), GreaterThan.class,
dateTimeSlot);
+ assertInferredBound(disjunctions.get(1), LessThan.class, dateTimeSlot);
+ }
+
+ private Substring substring(Expression source, int position, int length) {
+ return new Substring(source, new IntegerLiteral(position), new
IntegerLiteral(length));
+ }
+
+ private Expression rewrite(Expression expression) {
+ return
InferPredicateFromMonotonicFunction.inferForPartitionPrune(typeCoercion(expression));
+ }
+
+ private void assertOriginalPreserved(Expression rewritten, Expression
original) {
+ Expression analyzedOriginal = typeCoercion(original);
+
Assertions.assertTrue(ExpressionUtils.extractConjunction(rewritten).stream()
+ .anyMatch(conjunct -> conjunct.equals(analyzedOriginal) &&
!conjunct.isInferred()));
+ }
+
+ private void assertInferredBound(Expression expression,
+ Class<? extends ComparisonPredicate> predicateType, Expression
source) {
+ findInferredBound(expression, predicateType, source);
+ }
+
+ private ComparisonPredicate findInferredBound(Expression expression,
+ Class<? extends ComparisonPredicate> predicateType, Expression
source) {
+ List<Expression> conjuncts =
ExpressionUtils.extractConjunction(expression);
+ return (ComparisonPredicate) conjuncts.stream()
+ .filter(conjunct -> predicateType.isInstance(conjunct)
+ && conjunct.child(0).equals(source) &&
conjunct.isInferred())
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("missing inferred " +
predicateType.getSimpleName()
+ + " bound on " + source + " in " + expression));
+ }
+}
diff --git
a/regression-test/data/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.out
b/regression-test/data/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.out
new file mode 100644
index 00000000000..dfa61163525
--- /dev/null
+++
b/regression-test/data/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.out
@@ -0,0 +1,4 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !date_ceil_upper_bound --
+2021-01-01T00:00
+
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/always_mono_func.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/always_mono_func.groovy
index 82c3795a7b2..1be795d1037 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/always_mono_func.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/always_mono_func.groovy
@@ -123,11 +123,11 @@ suite("always_mono_func") {
// year
explain {
sql """ select * from always_mono_func where year(dt) >= 2019 """
- contains("partitions=3/5 (p3,p4,p5)")
+ contains("partitions=2/5 (p4,p5)")
}
explain {
sql """select * from always_mono_func where year(dt) < 2019 and
year(dt) > 2017"""
- contains("partitions=2/5 (p2,p3)")
+ contains("partitions=1/5 (p3)")
}
explain {
sql """select * from always_mono_func where year(dt) <2023"""
@@ -140,15 +140,15 @@ suite("always_mono_func") {
}
explain {
sql """select * from always_mono_func where to_monday(dt)
='2019-01-01' """
- contains("partitions=2/5 (p1,p4)")
+ contains("partitions=1/5 (p4)")
}
explain {
sql """select * from always_mono_func where to_monday(dt)
>='2018-01-01' and to_monday(dt) <'2019-01-01' """
- contains("partitions=4/5 (p1,p2,p3,p4)")
+ contains("partitions=2/5 (p3,p4)")
}
explain {
sql """ select * from always_mono_func where to_monday(dt) >=
"2019-01-01" """
- contains("partitions=3/5 (p1,p4,p5)")
+ contains("partitions=2/5 (p4,p5)")
}
// to_date
explain {
@@ -169,7 +169,7 @@ suite("always_mono_func") {
}
explain {
sql """select * from always_mono_func where to_date(dt) >='2018-01-01'
and to_date(dt) <'2019-01-01' """
- contains("partitions=2/5 (p2,p3)")
+ contains("partitions=1/5 (p3)")
}
explain {
sql """ select * from always_mono_func where to_date(dt) >
"2019-01-01" """
@@ -218,4 +218,4 @@ suite("always_mono_func") {
sql """select * from always_mono_func where
date_format(date_trunc(last_day(to_monday(dt)),'month'), 'yyyyMMdd') >
"20190101" """
contains("partitions=3/5 (p1,p4,p5)")
}
-}
\ No newline at end of file
+}
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/multi_column_range_partition.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/multi_column_range_partition.groovy
index 7869e1f3ec2..f401b64606b 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/multi_column_range_partition.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/multi_column_range_partition.groovy
@@ -71,7 +71,7 @@ suite("multi_column_range_partition") {
}
explain {
sql "select * from t_multi_column_range_partition where a=10 and
date_trunc(dt, 'day') ='2024-01-10'"
- contains("partitions=2/5 (p0,p10)")
+ contains("partitions=1/5 (p10)")
}
explain {
sql "select * from t_multi_column_range_partition where a<19 and
(date_trunc(dt, 'day') <'2024-01-20' OR date_trunc(dt, 'day') >'2024-02-10')"
@@ -211,7 +211,7 @@ suite("multi_column_range_partition") {
}
explain {
sql "SELECT * FROM t_multi_column_partition_datetime_first WHERE
date_trunc(dt,'day')='2024-1-10 00:00:00' and a >100"
- contains("partitions=2/5 (p0,p10)")
+ contains("partitions=1/5 (p10)")
}
explain {
sql "SELECT * FROM t_multi_column_partition_datetime_first WHERE
date_trunc(dt,'day')='2024-1-1 00:00:00' and a <0"
@@ -257,4 +257,4 @@ suite("multi_column_range_partition") {
contains("partitions=2/5 (p0,p10)")
}
}
-}
\ No newline at end of file
+}
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/one_col_range_partition.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/one_col_range_partition.groovy
index 4aa880e9c79..b29edd8c527 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/one_col_range_partition.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/one_col_range_partition.groovy
@@ -79,7 +79,7 @@ suite("one_col_range_partition") {
explain {
sql """SELECT count(*) FROM one_col_range_partition_date WHERE
(dt>='2020-10-01 00:00:00' or dt<='2020-9-01 00:00:00' ) and
(date_trunc(dt,'month')>='2020-9-01' or date_trunc(dt,'month')<='2020-6-01' )"""
- contains("partitions=327/1826")
+ contains("partitions=326/1826")
}
// !
@@ -324,4 +324,4 @@ suite("one_col_range_partition") {
contains("partitions=417/1826")
}
-}
\ No newline at end of file
+}
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_test.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_test.groovy
index e008d186485..d68f9cf7340 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_test.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_test.groovy
@@ -83,11 +83,11 @@ suite("one_key_range_part_test") {
}
explain {
sql("SELECT a, dt, c FROM key_1_fixed_range_date_part WHERE
date_trunc('month', dt) = '2023-03-01 00:00:00';")
- contains "2/14 (p_202302,p_202303)"
+ contains "1/14 (p_202303)"
}
explain {
sql("SELECT a, dt, c FROM key_1_fixed_range_date_part WHERE
date_trunc('month', dt) BETWEEN '2023-04-01 00:00:00' AND '2023-05-01
00:00:00';")
- contains "3/14 (p_202303,p_202304,p_202305)"
+ contains "2/14 (p_202304,p_202305)"
}
explain {
sql("SELECT a, dt, c FROM key_1_fixed_range_date_part WHERE
date_add(dt, INTERVAL 1 MONTH) = '2023-11-15 10:00:00';")
@@ -214,11 +214,11 @@ suite("one_key_range_part_test") {
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part WHERE
date_trunc('month', dt) = '2023-03-01 00:00:00';")
- contains "1/10 (p_202302)"
+ contains "0:VEMPTYSET"
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part WHERE
date_trunc('month', dt) BETWEEN '2023-06-01 00:00:00' AND '2023-07-01
00:00:00';")
- contains "2/10 (p_202305,p_202306)"
+ contains "1/10 (p_202306)"
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part WHERE
date_add(dt, INTERVAL 1 MONTH) = '2023-11-15 10:00:00';")
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_update_test.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_update_test.groovy
index 95d05e66f8d..4d037f431f0 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_update_test.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/one_key_range_part_update_test.groovy
@@ -76,11 +76,11 @@ suite("one_key_range_part_update_test") {
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part_update
WHERE date_trunc('month', dt) = '2023-08-01 00:00:00';")
- contains "2/11 (p_202306,p_202308)"
+ contains "1/11 (p_202308)"
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part_update
WHERE date_trunc('month', dt) BETWEEN '2023-06-01 00:00:00' AND '2023-08-01
00:00:00';")
- contains "3/11 (p_202305,p_202306,p_202308)"
+ contains "2/11 (p_202306,p_202308)"
}
explain {
sql("SELECT a, dt, c FROM key_1_special_fixed_range_date_part_update
WHERE date_add(dt, INTERVAL 1 MONTH) = '2023-09-15 10:00:00';")
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.groovy
index 77e59823d42..d822951585e 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/test_add_sub_diff_ceil_floor.groovy
@@ -191,6 +191,11 @@ suite("test_add_sub_diff_ceil_floor") {
sql """select * from test_add_sub_diff_ceil_floor_t where day_ceil(dt)
<'2019-01-01' """
contains("partitions=3/5 (p1,p2,p3)")
}
+ explain {
+ sql """select * from test_add_sub_diff_ceil_floor_t
+ where date_ceil(dt, interval 1 day) <'2019-01-01' """
+ contains("partitions=3/5 (p1,p2,p3)")
+ }
explain {
sql """select * from test_add_sub_diff_ceil_floor_t where
hour_ceil(dt) <'2019-01-01' """
contains("partitions=3/5 (p1,p2,p3)")
@@ -455,4 +460,22 @@ suite("test_add_sub_diff_ceil_floor") {
// sql """select * from max_t where weeks_diff(dt,
quarter(weeks_sub(dt, 1))) >'2020-01-01'"""
// contains("partitions=6/6 (p1,p2,p3,p4,p5,p6)")
//}
+
+ // Partition pruning already skips evaluation errors in partitions
excluded by a necessary
+ // condition. Keep that established behavior when the inferred condition
enables binary search.
+ sql "drop table if exists date_ceil_upper_bound_t"
+ sql """create table date_ceil_upper_bound_t (dt datetimev2 not null)
duplicate key(dt)
+ partition by range(dt) (
+ partition p_old values less than ('2022-01-01'),
+ partition p_max values less than MAXVALUE
+ ) distributed by hash(dt) buckets 1 properties('replication_num'='1');"""
+ sql """insert into date_ceil_upper_bound_t values
+ ('2021-01-01 00:00:00'), ('2021-12-31 00:00:00'), ('9999-12-31
23:59:59')"""
+ test {
+ sql """select date_ceil(dt, interval 1 day)
+ from date_ceil_upper_bound_t partition(p_max)"""
+ exception "out of range"
+ }
+ order_qt_date_ceil_upper_bound """select * from date_ceil_upper_bound_t
+ where date_ceil(dt, interval 1 day) <= '2021-01-01'"""
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]