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]

Reply via email to