This is an automated email from the ASF dual-hosted git repository.
zhangstar333 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 c899ceada94 [fix](nereids) Fix compound interval arithmetic operator
binding (#66965)
c899ceada94 is described below
commit c899ceada941807a5114e48a23a2c0a74bd664bd
Author: foxtail463 <[email protected]>
AuthorDate: Tue Aug 25 20:16:10 2026 +0800
[fix](nereids) Fix compound interval arithmetic operator binding (#66965)
Route interval arithmetic operators through DATE_ADD and DATE_SUB, and
add MICROSECOND handling to DatetimeFunctionBinder. This prevents
compound intervals from binding to invalid pluralized function names
while preserving simple interval behavior.
intro by #60347
---------
Co-authored-by: yangtao555 <[email protected]>
---
.../doris/nereids/parser/LogicalPlanBuilder.java | 10 +++---
.../rules/analysis/DatetimeFunctionBinder.java | 6 ++++
.../trees/expressions/literal/Interval.java | 5 +++
.../nereids/rules/analysis/BindExpressionTest.java | 36 ++++++++++++++++++++++
.../trees/expressions/literal/IntervalTest.java | 6 ++++
5 files changed, 59 insertions(+), 4 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 667724bb6bf..69d2a45d4f5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -3194,8 +3194,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
throw new ParseException("Only supported: " +
Operator.ADD, ctx);
}
Interval interval = (Interval) left;
- String funcOpName = String.format("%sS_ADD",
interval.timeUnit());
- return new UnboundFunction(funcOpName, ImmutableList.of(right,
interval.value()));
+ return buildDateArithmetic(right, interval, "ADD");
}
if (right instanceof Interval) {
@@ -3208,8 +3207,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
throw new ParseException("Only supported: " + Operator.ADD
+ " and " + Operator.SUBTRACT, ctx);
}
Interval interval = (Interval) right;
- String funcOpName = String.format("%sS_%s",
interval.timeUnit(), op);
- return new UnboundFunction(funcOpName, ImmutableList.of(left,
interval.value()));
+ return buildDateArithmetic(left, interval, op);
}
return ParserUtils.withOrigin(ctx, () -> {
@@ -3242,6 +3240,10 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
});
}
+ private static UnboundFunction buildDateArithmetic(Expression date,
Interval interval, String operation) {
+ return new UnboundFunction("DATE_" + operation, ImmutableList.of(date,
interval));
+ }
+
@Override
public Expression visitCurrentDate(DorisParser.CurrentDateContext ctx) {
return new CurrentDate();
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
index 4e1a768bd97..73d6556c483 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/DatetimeFunctionBinder.java
@@ -55,7 +55,9 @@ import
org.apache.doris.nereids.trees.expressions.functions.scalar.HourSecondSub
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.HoursSub;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.MicroSecondsAdd;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.MicroSecondsDiff;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.MicroSecondsSub;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteCeil;
import org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteFloor;
import
org.apache.doris.nereids.trees.expressions.functions.scalar.MinuteMicrosecondAdd;
@@ -328,6 +330,8 @@ public class DatetimeFunctionBinder {
return new MinutesAdd(timestamp, amount);
case SECOND:
return new SecondsAdd(timestamp, amount);
+ case MICROSECOND:
+ return new MicroSecondsAdd(timestamp, amount);
case YEAR_MONTH:
return new YearMonthAdd(timestamp, amount);
case DAY_SECOND:
@@ -374,6 +378,8 @@ public class DatetimeFunctionBinder {
return new MinutesSub(timeStamp, amount);
case SECOND:
return new SecondsSub(timeStamp, amount);
+ case MICROSECOND:
+ return new MicroSecondsSub(timeStamp, amount);
case YEAR_MONTH:
return new YearMonthSub(timeStamp, amount);
case DAY_SECOND:
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
index f490c225c44..597a14c371f 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Interval.java
@@ -70,6 +70,11 @@ public class Interval extends Expression implements
UnaryExpression, AlwaysNotNu
return visitor.visitInterval(this, context);
}
+ @Override
+ public String computeToSql() {
+ return "INTERVAL " + value().toSql() + " " + timeUnit;
+ }
+
@Override
public String toDigest() {
StringBuilder sb = new StringBuilder();
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindExpressionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindExpressionTest.java
index d2f9dffce37..fd890d53233 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindExpressionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindExpressionTest.java
@@ -20,6 +20,11 @@ package org.apache.doris.nereids.rules.analysis;
import org.apache.doris.nereids.pattern.GeneratedPlanPatterns;
import org.apache.doris.nereids.rules.RulePromise;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.scalar.DayHourAdd;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.HourSecondSub;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.MicroSecondsAdd;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.MicroSecondsSub;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.YearMonthAdd;
import org.apache.doris.nereids.trees.plans.JoinType;
import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.utframe.TestWithFeService;
@@ -101,6 +106,37 @@ class BindExpressionTest extends TestWithFeService
implements GeneratedPlanPatte
}
+ @Test
+ void testCompoundIntervalArithmetic() {
+ PlanChecker.from(connectContext)
+ .analyze("select cast(col1 as datetime) + interval '1-2'
year_month from t1")
+ .matches(any().when(plan -> plan.getExpressions().stream()
+ .anyMatch(expression ->
expression.anyMatch(YearMonthAdd.class::isInstance))));
+
+ PlanChecker.from(connectContext)
+ .analyze("select interval '1 5' day_hour + cast(col1 as
datetime) from t1")
+ .matches(any().when(plan -> plan.getExpressions().stream()
+ .anyMatch(expression ->
expression.anyMatch(DayHourAdd.class::isInstance))));
+
+ PlanChecker.from(connectContext)
+ .analyze("select cast(col1 as datetime) - interval '2 30'
hour_second from t1")
+ .matches(any().when(plan -> plan.getExpressions().stream()
+ .anyMatch(expression ->
expression.anyMatch(HourSecondSub.class::isInstance))));
+ }
+
+ @Test
+ void testSimpleIntervalArithmeticBinding() {
+ PlanChecker.from(connectContext)
+ .analyze("select cast(col1 as datetime) + interval 1
microsecond from t1")
+ .matches(any().when(plan -> plan.getExpressions().stream()
+ .anyMatch(expression ->
expression.anyMatch(MicroSecondsAdd.class::isInstance))));
+
+ PlanChecker.from(connectContext)
+ .analyze("select cast(col1 as datetime) - interval 1
microsecond from t1")
+ .matches(any().when(plan -> plan.getExpressions().stream()
+ .anyMatch(expression ->
expression.anyMatch(MicroSecondsSub.class::isInstance))));
+ }
+
@Test
void testFilterSort() {
String sql = "select * from t1 where t1.col2 = 1 order by col2";
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntervalTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntervalTest.java
index e0ddde5f7c0..963a5fdf678 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntervalTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/IntervalTest.java
@@ -29,4 +29,10 @@ public class IntervalTest {
Interval i2 = new Interval(new IntegerLiteral(1), TimeUnit.SECOND);
Assertions.assertNotEquals(i1, i2);
}
+
+ @Test
+ public void testIntervalToSql() {
+ Interval interval = new Interval(new IntegerLiteral(1), TimeUnit.DAY);
+ Assertions.assertEquals("INTERVAL 1 DAY", interval.toSql());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]