DateCeilFloorMonotonic.java
// 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.IntegerLikeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
import java.util.Optional;
import java.util.function.BiFunction;
/** 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;
}
}
@Override
default boolean isPositive() {
return true;
}
@Override
default int getMonotonicFunctionChildIndex() {
return 0;
}
/**
* Shared preimage upper bound for the FLOOR subclasses (NOT ceil): floor(col) = c <=> col in
* [c, c + period units). The period is the second arg (default 1 when absent); addFn builds the
* unit-specific "+n" expression (e.g. HoursAdd::new). Returns empty when the period is not an
* integer literal or an origin (3rd arg) is present -- an origin shifts bucket alignment and no
* engine reverse-solves through it, so we conservatively skip the upper bound (lower bound stays
* sound). Only meaningful when the subclass also overrides isFloor() to true.
*/
default Optional<Expression> floorUpperBoundByPeriod(Literal c,
BiFunction<Expression, Expression, Expression> addFn) {
switch (arity()) {
case 1:
return Optional.of(addFn.apply(c, new IntegerLiteral(1)));
case 2:
Expression period = child(1);
// BE rejects period < 1 at runtime (function_datetime_floor_ceil.cpp), so a non-positive
// period is not a valid bucket width -- skip the preimage rather than build a bogus bound.
if (period instanceof IntegerLikeLiteral && ((IntegerLikeLiteral) period).getIntValue() > 0) {
return Optional.of(addFn.apply(c, period));
}
return Optional.empty();
default:
// 3-arg (with origin) or unexpected arity: only the lower bound is derived
return Optional.empty();
}
}
/**
* Shared preimage lower bound for the CEIL subclasses (NOT floor): ceil(col) = c <=> col in
* (c - period units, c]. Mirror of {@link #floorUpperBoundByPeriod}. subFn builds the
* unit-specific "-n" expression (e.g. HoursSub::new). Returns empty when the period is not an
* integer literal or an origin (3rd arg) is present. IMPORTANT: an origin can also make the ceil
* property itself fail (the month/quarter/year day-of-month clamp fights ceil), so the caller
* must additionally gate isCeil() to the no-origin forms; this method only builds the =-preimage.
* Only meaningful when the subclass also overrides isCeil() to true.
*/
default Optional<Expression> ceilLowerBoundByPeriod(Literal c,
BiFunction<Expression, Expression, Expression> subFn) {
switch (arity()) {
case 1:
return Optional.of(subFn.apply(c, new IntegerLiteral(1)));
case 2:
Expression period = child(1);
// BE rejects period < 1 at runtime (function_datetime_floor_ceil.cpp), so a non-positive
// period is not a valid bucket width -- skip the preimage rather than build a bogus bound.
if (period instanceof IntegerLikeLiteral && ((IntegerLikeLiteral) period).getIntValue() > 0) {
return Optional.of(subFn.apply(c, period));
}
return Optional.empty();
default:
// 3-arg (with origin) or unexpected arity: only the upper bound is derived
return Optional.empty();
}
}
}