This is an automated email from the ASF dual-hosted git repository.

mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 51d617dbf8 [CALCITE-7216] SqlOperator.inferReturnType throws the wrong 
exception on error
51d617dbf8 is described below

commit 51d617dbf80c2a67582fa6529be81c0c722716a4
Author: Mihai Budiu <[email protected]>
AuthorDate: Wed Oct 1 13:51:41 2025 -0700

    [CALCITE-7216] SqlOperator.inferReturnType throws the wrong exception on 
error
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../main/java/org/apache/calcite/rel/core/Uncollect.java    |  2 +-
 .../java/org/apache/calcite/runtime/CalciteResource.java    |  3 +++
 core/src/main/java/org/apache/calcite/sql/SqlOperator.java  |  7 ++++---
 .../java/org/apache/calcite/sql/fun/SqlBetweenOperator.java | 10 +++++++---
 .../org/apache/calcite/sql/fun/SqlCoalesceFunction.java     |  7 ++++---
 .../org/apache/calcite/sql/fun/SqlLibraryOperators.java     | 13 +++++++++----
 .../org/apache/calcite/sql/fun/SqlStdOperatorTable.java     | 13 ++++++++++---
 .../org/apache/calcite/runtime/CalciteResource.properties   |  1 +
 .../test/java/org/apache/calcite/rex/RexProgramTest.java    |  6 ++++--
 .../test/java/org/apache/calcite/test/RelBuilderTest.java   |  8 ++++----
 .../test/java/org/apache/calcite/test/SqlValidatorTest.java |  9 +++++++++
 .../main/java/org/apache/calcite/test/SqlOperatorTest.java  | 10 ++++++++++
 12 files changed, 66 insertions(+), 23 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java 
b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
index 24f406b3eb..1fa71fc90b 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Uncollect.java
@@ -201,7 +201,7 @@ public static RelDataType deriveUncollectRowType(RelNode 
rel,
     return builder.build();
   }
 
-  /** Get the aliases for the unnest items. */
+  /** Gets the aliases for the unnest items. */
   public List<String> getItemAliases() {
     return itemAliases;
   }
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java 
b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 2eaf952d15..1ec9030784 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -1156,4 +1156,7 @@ ExInst<RuntimeException> 
multipleCapturingGroupsForRegexpFunctions(String value,
 
   @BaseMessage("Index in ROW type does not have a constant integer or string 
value")
   ExInst<SqlValidatorException> illegalRowIndex();
+
+  @BaseMessage("Cannot infer return type for {0}; operand types: {1}")
+  ExInst<SqlValidatorException> cannotInferReturnType(String operator, String 
types);
 }
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java 
b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
index de8b70afd6..dc8f097709 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java
@@ -561,9 +561,10 @@ public RelDataType inferReturnType(
     if (returnTypeInference != null) {
       RelDataType returnType = returnTypeInference.inferReturnType(opBinding);
       if (returnType == null) {
-        throw new IllegalArgumentException("Cannot infer return type for "
-            + opBinding.getOperator() + "; operand types: "
-            + opBinding.collectOperandTypes());
+        throw opBinding.newError(
+            RESOURCE.cannotInferReturnType(
+                opBinding.getOperator().toString(),
+                opBinding.collectOperandTypes().toString()));
       }
 
       if (operandTypeInference != null
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java
index b040b31d3e..6f6234c3ed 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBetweenOperator.java
@@ -39,8 +39,6 @@
 
 import static org.apache.calcite.util.Static.RESOURCE;
 
-import static java.util.Objects.requireNonNull;
-
 /**
  * Defines the BETWEEN operator.
  *
@@ -150,7 +148,13 @@ private static SqlBetweenOperator of(boolean negated, 
boolean symmetric) {
             opBinding.collectOperandTypes());
     RelDataType type =
         ReturnTypes.BOOLEAN_NULLABLE.inferReturnType(newOpBinding);
-    return requireNonNull(type, "inferred BETWEEN element type");
+    if (type == null) {
+      throw opBinding.newError(
+          RESOURCE.cannotInferReturnType(
+              opBinding.getOperator().toString(),
+              opBinding.collectOperandTypes().toString()));
+    }
+    return type;
   }
 
   @Override public String getSignatureTemplate(final int operandsCount) {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlCoalesceFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlCoalesceFunction.java
index b4af37b32d..4a3fc60282 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCoalesceFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCoalesceFunction.java
@@ -120,9 +120,10 @@ public SqlCoalesceFunction() {
     }
 
     if (returnType == null) {
-      throw new IllegalArgumentException(
-          "Cannot infer return type for " + opBinding.getOperator() + "; 
operand types: "
-              + opBinding.collectOperandTypes());
+      throw opBinding.newError(
+          RESOURCE.cannotInferReturnType(
+              opBinding.getOperator().toString(),
+              opBinding.collectOperandTypes().toString()));
     }
 
     return returnType;
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 0df863630b..f8f4e9273b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -1380,7 +1380,12 @@ private static RelDataType 
arrayReturnType(SqlOperatorBinding opBinding) {
               ? ReturnTypes.LEAST_RESTRICTIVE.inferReturnType(opBinding)
               : opBinding.getTypeFactory().createUnknownType();
     }
-    requireNonNull(type, "inferred array element type");
+    if (type == null) {
+      throw opBinding.newError(
+          RESOURCE.cannotInferReturnType(
+              opBinding.getOperator().toString(),
+              opBinding.collectOperandTypes().toString()));
+    }
 
     // explicit cast elements to component type if they are not same
     SqlValidatorUtil.adjustTypeForArrayConstructor(type, opBinding);
@@ -1577,8 +1582,8 @@ private static RelDataType 
arrayInsertReturnType(SqlOperatorBinding opBinding) {
     requireNonNull(componentType, () -> "componentType of " + arrayType);
 
     // we don't need to do leastRestrictive on componentType and elementType,
-    // because in operand checker we limit the elementType must equals array 
component type.
-    // So we use componentType directly.
+    // because in operand checker we limit the elementType such that it equals 
the array component
+    // type. So we use componentType directly.
     RelDataType type =
         opBinding.getTypeFactory().leastRestrictive(
             ImmutableList.of(componentType, elementType2));
@@ -1588,7 +1593,7 @@ private static RelDataType 
arrayInsertReturnType(SqlOperatorBinding opBinding) {
     // position is large", it implies that in the result the element type is 
always nullable.
     type = opBinding.getTypeFactory().createTypeWithNullability(type, true);
     // make explicit CAST for array elements and inserted element to the 
biggest type
-    // if array component type not equals to inserted element type
+    // if array component type is not equal to the inserted element type
     if (!componentType.equalsSansFieldNamesAndNullability(elementType2)) {
       // For array_insert, 0 is the array arg and 2 is the inserted element
       SqlValidatorUtil.
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 029e62bf10..1f42de7c1f 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.sql.fun;
 
 import org.apache.calcite.avatica.util.TimeUnit;
+import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlAsOperator;
 import org.apache.calcite.sql.SqlBasicCall;
@@ -86,6 +87,7 @@
 import java.util.function.Supplier;
 
 import static org.apache.calcite.linq4j.Nullness.castNonNull;
+import static org.apache.calcite.util.Static.RESOURCE;
 
 import static java.util.Objects.requireNonNull;
 
@@ -273,9 +275,14 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
                 typeToTransform.getSqlTypeName().getFamily() == 
SqlTypeFamily.ARRAY
                     ? ReturnTypes.LEAST_RESTRICTIVE
                     : ReturnTypes.DYADIC_STRING_SUM_PRECISION_NULLABLE;
-
-            return requireNonNull(returnType.inferReturnType(opBinding),
-                "inferred CONCAT element type");
+            RelDataType type = returnType.inferReturnType(opBinding);
+            if (type == null) {
+              throw opBinding.newError(
+                  RESOURCE.cannotInferReturnType(
+                      opBinding.getOperator().toString(),
+                      opBinding.collectOperandTypes().toString()));
+            }
+            return type;
           }),
           null,
           OperandTypes.STRING_SAME_SAME_OR_ARRAY_SAME_SAME);
diff --git 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 3628d6a982..f917c2aa4b 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -378,4 +378,5 @@ AsofCannotBeCorrelated=ASOF JOIN does not support 
correlated subqueries
 UnknownRowField=ROW type does not have a field named ''{0}'': {1}
 IllegalRowIndexValue=ROW type does not have a field with index {0,number}; 
legal range is 1 to {1,number}
 IllegalRowIndex=Index in ROW type does not have a constant integer or string 
value
+CannotInferReturnType=Cannot infer return type for {0}; operand types: {1}
 # End CalciteResource.properties
diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java 
b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
index c9d5b278cf..11493cfb1e 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
@@ -24,6 +24,7 @@
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeImpl;
+import org.apache.calcite.runtime.CalciteException;
 import org.apache.calcite.sql.SqlBasicFunction;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
@@ -74,6 +75,7 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.hasToString;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -521,10 +523,10 @@ private RexProgramBuilder createProg(int variant) {
     try {
       final RexNode node = coalesce(vVarchar(1), vInt(2));
       fail("expected exception, got " + node);
-    } catch (IllegalArgumentException e) {
+    } catch (CalciteException e) {
       final String expected = "Cannot infer return type for COALESCE;"
           + " operand types: [VARCHAR, INTEGER]";
-      assertThat(e.getMessage(), is(expected));
+      assertThat(e.getMessage(), containsString(expected));
     }
   }
 
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 9ca3eec4c8..eb4f719991 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -774,9 +774,9 @@ private void checkSimplify(UnaryOperator<RelBuilder.Config> 
transform,
           builder.call(SqlStdOperatorTable.PLUS, builder.field(1),
               builder.field(3));
       fail("expected error, got " + call);
-    } catch (IllegalArgumentException e) {
+    } catch (CalciteException e) {
       assertThat(e.getMessage(),
-          is("Cannot infer return type for +; "
+          containsString("Cannot infer return type for +; "
               + "operand types: [VARCHAR(10), SMALLINT]"));
     }
   }
@@ -4389,7 +4389,7 @@ private static RelBuilder assertSize(RelBuilder b,
     try {
       builder.call(SqlStdOperatorTable.PLUS, Lists.newArrayList(arg0, arg1));
       fail("Invalid combination of parameter types");
-    } catch (IllegalArgumentException e) {
+    } catch (CalciteException e) {
       assertThat(e.getMessage(), containsString("Cannot infer return type"));
     }
 
@@ -4397,7 +4397,7 @@ private static RelBuilder assertSize(RelBuilder b,
     try {
       builder.call(SqlStdOperatorTable.PLUS, arg0, arg1);
       fail("Invalid combination of parameter types");
-    } catch (IllegalArgumentException e) {
+    } catch (CalciteException e) {
       assertThat(e.getMessage(), containsString("Cannot infer return type"));
     }
   }
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 29c5a32031..b1c7642109 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -5789,6 +5789,15 @@ private SqlValidatorFixture 
joinCommonColumnsFixture(boolean caseSensitive) {
         .type(type1);
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7216";>[CALCITE-7216]
+   * SqlOperator.inferReturnType throws the wrong exception on error</a>. */
+  @Test void testWrongException() {
+    sql("select ^least(DATE '2020-01-01', 'x')^")
+        .withConformance(SqlConformanceEnum.BIG_QUERY)
+        .withOperatorTable(operatorTableFor(SqlLibrary.BIG_QUERY))
+        .fails("Cannot infer return type for LEAST; operand types: \\[DATE, 
CHAR\\(1\\)\\]");
+  }
+
   @Test void testNaturalJoinIncompatibleDatatype() {
     sql("select *\n"
         + "from (select ename as name, hiredate as deptno from emp)\n"
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 8d6fd35361..f10bc70879 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -12165,6 +12165,16 @@ void assertSubFunReturns(boolean binary, String s, int 
start,
     f0.forEachLibrary(libraries, consumer);
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7216";>[CALCITE-7216]
+   * SqlOperator.inferReturnType throws the wrong exception on error</a>. */
+  @Test void testLeastMismatch() {
+    final SqlOperatorFixture f = fixture()
+        .setFor(SqlLibraryOperators.LEAST, VmName.EXPAND)
+        .withLibrary(SqlLibrary.BIG_QUERY);
+    f.checkFails("^least(DATE '2020-01-01', 'x')^",
+        "Cannot infer return type for LEAST; operand types: \\[DATE, 
CHAR\\(1\\)\\]", false);
+  }
+
   @Test void testIfNullFunc() {
     final SqlOperatorFixture f = fixture();
     f.setFor(SqlLibraryOperators.IFNULL, VmName.EXPAND);

Reply via email to