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 a2e9094466b [fix](function) Align interval folding with runtime 
(#67840)
a2e9094466b is described below

commit a2e9094466b406ff87d44203f9bd7ac961a5b144
Author: morrySnow <[email protected]>
AuthorDate: Mon Sep 14 10:52:39 2026 +0800

    [fix](function) Align interval folding with runtime (#67840)
    
    ## Problem
    
    `INTERVAL` could return different results for the same repeated
    thresholds depending on whether the expression was constant-folded in
    the frontend or evaluated by the backend.
    
    For example, the folded expressions below returned `1, 2, 2`, while
    equivalent expressions using a `numbers()` column returned `2, 3, 3`:
    
    ```sql
    SELECT INTERVAL(0, 0, 0),
           INTERVAL(0, 0, 0, 0),
           INTERVAL(1, 0, 1, 1, 2);
    ```
    
    ## Root cause
    
    Frontend constant folding used `Arrays.binarySearch`. When a sorted
    threshold array contains duplicate values, Java may return any matching
    position. Backend execution uses upper-bound semantics and continues
    past all thresholds less than or equal to the comparison value.
    
    ## Fix
    
    Replace frontend `binarySearch` with the same upper-bound binary-search
    loop used by backend execution. Existing behavior for `NULL` comparison
    values and `NULL` thresholds remains unchanged.
    
    ## Tests
    
    - Added executable-function unit tests for repeated thresholds, empty
    thresholds, lower/upper boundaries, and `NULL` values.
    - Added regression coverage comparing frontend-folded expressions with
    equivalent backend-evaluated expressions.
    - Full frontend build and checkstyle passed.
    - The focused unit test passed with 4 tests and no failures.
    - The new regression suite passed.
---
 .../functions/executable/NumericArithmetic.java    | 18 ++++++-----
 .../executable/NumericArithmeticTest.java          | 32 +++++++++++++++++++
 .../interval_constant_fold_consistency.out         | 10 ++++++
 .../interval_constant_fold_consistency.groovy      | 36 ++++++++++++++++++++++
 4 files changed, 88 insertions(+), 8 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java
index 6f664e2fadf..507d521e310 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmetic.java
@@ -46,7 +46,6 @@ import org.apache.commons.math3.util.FastMath;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
 
 /**
  * executable functions:
@@ -1136,13 +1135,16 @@ public class NumericArithmetic {
             }
         }
 
-        int pos = Arrays.binarySearch(thresholdValues, value);
-
-        if (pos >= 0) {
-            return new IntegerLiteral(pos + 1);
-        } else {
-            int insertionPoint = -(pos + 1);
-            return new IntegerLiteral(insertionPoint);
+        int low = 0;
+        int high = thresholdValues.length;
+        while (low < high) {
+            int mid = low + ((high - low) >>> 1);
+            if (thresholdValues[mid] <= value) {
+                low = mid + 1;
+            } else {
+                high = mid;
+            }
         }
+        return new IntegerLiteral(low);
     }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmeticTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmeticTest.java
index 749a57e8b5d..b2eb7532af1 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmeticTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/NumericArithmeticTest.java
@@ -17,10 +17,14 @@
 
 package org.apache.doris.nereids.trees.expressions.functions.executable;
 
+import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DecimalV3Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.types.DecimalV2Type;
 import org.apache.doris.nereids.types.DecimalV3Type;
 
@@ -54,4 +58,32 @@ public class NumericArithmeticTest {
         Assertions.assertEquals(BooleanLiteral.FALSE, 
NumericArithmetic.signbit(new DoubleLiteral(1.0)));
         Assertions.assertEquals(BooleanLiteral.TRUE, 
NumericArithmetic.signbit(new DoubleLiteral(-1.0)));
     }
+
+    @Test
+    public void testIntervalUsesUpperBoundForRepeatedThresholds() {
+        assertInterval(0, 0);
+        assertInterval(0, -3, -2, -1);
+        assertInterval(2, 0, 0, 0);
+        assertInterval(3, 0, 0, 0, 0);
+        assertInterval(3, 1, 0, 1, 1, 2);
+        assertInterval(4, 3, 0, 1, 1, 2);
+
+        IntegerLiteral nullThresholdResult = (IntegerLiteral) 
NumericArithmetic.interval(
+                new BigIntLiteral(0), NullLiteral.INSTANCE, new 
BigIntLiteral(0));
+        Assertions.assertEquals(2, nullThresholdResult.getValue());
+
+        IntegerLiteral nullCompareResult = (IntegerLiteral) 
NumericArithmetic.interval(
+                NullLiteral.INSTANCE, new BigIntLiteral(0));
+        Assertions.assertEquals(-1, nullCompareResult.getValue());
+    }
+
+    private void assertInterval(int expected, long compareValue, long... 
thresholds) {
+        Literal[] thresholdLiterals = new Literal[thresholds.length];
+        for (int i = 0; i < thresholds.length; i++) {
+            thresholdLiterals[i] = new BigIntLiteral(thresholds[i]);
+        }
+        IntegerLiteral result = (IntegerLiteral) NumericArithmetic.interval(
+                new BigIntLiteral(compareValue), thresholdLiterals);
+        Assertions.assertEquals(expected, result.getValue());
+    }
 }
diff --git 
a/regression-test/data/nereids_function_p0/scalar_function/interval_constant_fold_consistency.out
 
b/regression-test/data/nereids_function_p0/scalar_function/interval_constant_fold_consistency.out
new file mode 100644
index 00000000000..c250eb0b303
--- /dev/null
+++ 
b/regression-test/data/nereids_function_p0/scalar_function/interval_constant_fold_consistency.out
@@ -0,0 +1,10 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !interval_folded --
+2      3       3
+
+-- !interval_runtime --
+2      3       3
+
+-- !interval_boundaries --
+-1     0       4       2
+
diff --git 
a/regression-test/suites/nereids_function_p0/scalar_function/interval_constant_fold_consistency.groovy
 
b/regression-test/suites/nereids_function_p0/scalar_function/interval_constant_fold_consistency.groovy
new file mode 100644
index 00000000000..7c9bfa8301b
--- /dev/null
+++ 
b/regression-test/suites/nereids_function_p0/scalar_function/interval_constant_fold_consistency.groovy
@@ -0,0 +1,36 @@
+// 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.
+
+suite("interval_constant_fold_consistency") {
+    qt_interval_folded """
+        select interval(0, 0, 0), interval(0, 0, 0, 0), interval(1, 0, 1, 1, 2)
+    """
+
+    qt_interval_runtime """
+        select interval(number * 0, 0, 0),
+               interval(number * 0, 0, 0, 0),
+               interval(number * 0 + 1, 0, 1, 1, 2)
+        from numbers("number" = "1")
+    """
+
+    qt_interval_boundaries """
+        select interval(null, 0),
+               interval(-3, -2, -1),
+               interval(3, 0, 1, 1, 2),
+               interval(0, null, 0)
+    """
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to