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);