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 8f8c9256499d..8549e1bb9216 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
@@ -1240,9 +1240,7 @@ private static RelDataType arrayCompactReturnType(SqlOperatorBinding opBinding)
public static final SqlFunction ARRAY_CONTAINS =
SqlBasicFunction.create(SqlKind.ARRAY_CONTAINS,
ReturnTypes.BOOLEAN_NULLABLE,
- OperandTypes.and(
- OperandTypes.NONNULL_NONNULL_NOT_CAST,
- OperandTypes.ARRAY_ELEMENT));
+ OperandTypes.ARRAY_ELEMENT_NONNULL);
/** The "ARRAY_DISTINCT(array)" function. */
@LibraryOperator(libraries = {SPARK})
@@ -1257,7 +1255,7 @@ private static RelDataType arrayCompactReturnType(SqlOperatorBinding opBinding)
SqlBasicFunction.create(SqlKind.ARRAY_EXCEPT,
ReturnTypes.LEAST_RESTRICTIVE,
OperandTypes.and(
- OperandTypes.NONNULL_NONNULL_NOT_CAST,
+ OperandTypes.NONNULL_NONNULL,
OperandTypes.SAME_SAME,
OperandTypes.family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY)));
@@ -1290,7 +1288,7 @@ private static RelDataType arrayInsertReturnType(SqlOperatorBinding opBinding) {
SqlBasicFunction.create(SqlKind.ARRAY_INTERSECT,
ReturnTypes.LEAST_RESTRICTIVE,
OperandTypes.and(
- OperandTypes.NONNULL_NONNULL_NOT_CAST,
+ OperandTypes.NONNULL_NONNULL,
OperandTypes.SAME_SAME,
OperandTypes.family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY)));
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ArrayElementOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/ArrayElementOperandTypeChecker.java
index 8291b1681d28..bed38a73e65a 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ArrayElementOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ArrayElementOperandTypeChecker.java
@@ -21,6 +21,7 @@
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperandCountRange;
import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlUtil;
import com.google.common.collect.ImmutableList;
@@ -31,11 +32,41 @@
* Parameter type-checking strategy where types must be Array and Array element type.
*/
public class ArrayElementOperandTypeChecker implements SqlOperandTypeChecker {
+ //~ Instance fields --------------------------------------------------------
+
+ private final boolean allowNullCheck;
+ private final boolean allowCast;
+
+ //~ Constructors -----------------------------------------------------------
+
+ public ArrayElementOperandTypeChecker() {
+ this.allowNullCheck = false;
+ this.allowCast = false;
+ }
+
+ public ArrayElementOperandTypeChecker(boolean allowNullCheck, boolean allowCast) {
+ this.allowNullCheck = allowNullCheck;
+ this.allowCast = allowCast;
+ }
+
//~ Methods ----------------------------------------------------------------
@Override public boolean checkOperandTypes(
SqlCallBinding callBinding,
boolean throwOnFailure) {
+ if (allowNullCheck) {
+ // no operand can be null for type-checking to succeed
+ for (SqlNode node : callBinding.operands()) {
+ if (SqlUtil.isNullLiteral(node, allowCast)) {
+ if (throwOnFailure) {
+ throw callBinding.getValidator().newValidationError(node, RESOURCE.nullIllegal());
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+
final SqlNode op0 = callBinding.operand(0);
if (!OperandTypes.ARRAY.checkSingleOperandType(
callBinding,
diff --git a/core/src/main/java/org/apache/calcite/sql/type/NullOperandTypeChecker.java b/core/src/main/java/org/apache/calcite/sql/type/NullOperandTypeChecker.java
index cdd7862b670f..7810f8d2914f 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/NullOperandTypeChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/NullOperandTypeChecker.java
@@ -23,8 +23,7 @@
import static org.apache.calcite.util.Static.RESOURCE;
/**
- * Parameter type-checking strategy type must not be a NULL (including NULL
,
- * CAST(NULL as ...)
but not CAST(CAST(NULL as ...) AS ...)
).
+ * Parameter type-checking strategy where all operand types must not be NULL.
*/
public class NullOperandTypeChecker extends SameOperandTypeChecker {
//~ Instance fields --------------------------------------------------------
@@ -42,7 +41,7 @@ public NullOperandTypeChecker(final int nOperands, final boolean allowCast) {
@Override public boolean checkOperandTypes(final SqlCallBinding callBinding,
final boolean throwOnFailure) {
- // all operands can't be null
+ // no operand can be null for type-checking to succeed
for (SqlNode node : callBinding.operands()) {
if (SqlUtil.isNullLiteral(node, allowCast)) {
if (throwOnFailure) {
diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
index 0b046acceb93..a9b8770ee6e4 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
@@ -614,6 +614,9 @@ public static SqlOperandTypeChecker variadic(
public static final SqlOperandTypeChecker ARRAY_ELEMENT =
new ArrayElementOperandTypeChecker();
+ public static final SqlOperandTypeChecker ARRAY_ELEMENT_NONNULL =
+ new ArrayElementOperandTypeChecker(true, false);
+
public static final SqlOperandTypeChecker ARRAY_INSERT =
new ArrayInsertOperandTypeChecker();
@@ -639,9 +642,9 @@ public static SqlOperandTypeChecker variadic(
new LiteralOperandTypeChecker(false);
/**
- * Operand type-checking strategy type must be a non-NULL value without cast.
+ * Operand type-checking strategy type must be a non-NULL value.
*/
- public static final SqlSingleOperandTypeChecker NONNULL_NONNULL_NOT_CAST =
+ public static final SqlSingleOperandTypeChecker NONNULL_NONNULL =
new NullOperandTypeChecker(2, false);
/**
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 67d80c6b1a31..8e15b0af049f 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -6417,20 +6417,13 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
// library (i.e. "fun=flink") we could add a function with Flink behavior.
f.checkNull("array_contains(array[1, null], cast(null as integer))");
f.checkType("array_contains(array[1, null], cast(null as integer))", "BOOLEAN");
- f.checkFails("^array_contains(array[1, 2], true)^", "Cannot apply 'ARRAY_CONTAINS' to arguments"
- + " of type 'ARRAY_CONTAINS\\(, \\)'\\. Supported form\\(s\\): "
- + "'ARRAY_CONTAINS\\(, \\)'", false);
+ f.checkFails("^array_contains(array[1, 2], true)^",
+ "INTEGER is not comparable to BOOLEAN", false);
// check null without cast
- f.checkFails("^array_contains(array[1, null], null)^", "Cannot apply 'ARRAY_CONTAINS' to "
- + "arguments of type 'ARRAY_CONTAINS\\(, \\)'\\. Supported form\\(s\\):"
- + " 'ARRAY_CONTAINS\\(, \\)'", false);
- f.checkFails("^array_contains(null, array[1, null])^", "Cannot apply 'ARRAY_CONTAINS' to "
- + "arguments of type 'ARRAY_CONTAINS\\(, \\)'\\. Supported form\\(s\\):"
- + " 'ARRAY_CONTAINS\\(, \\)'", false);
- f.checkFails("^array_contains(array[1, 2], null)^", "Cannot apply 'ARRAY_CONTAINS' to "
- + "arguments of type 'ARRAY_CONTAINS\\(, \\)'\\. Supported form\\(s\\):"
- + " 'ARRAY_CONTAINS\\(, \\)'", false);
+ f.checkFails("array_contains(array[1, 2], ^null^)", "Illegal use of 'NULL'", false);
+ f.checkFails("array_contains(^null^, array[1, 2])", "Illegal use of 'NULL'", false);
+ f.checkFails("array_contains(^null^, null)", "Illegal use of 'NULL'", false);
}
/** Tests {@code ARRAY_DISTINCT} function from Spark. */
@@ -6801,6 +6794,10 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
"Cannot apply 'ARRAY_EXCEPT' to arguments of type 'ARRAY_EXCEPT\\(, "
+ "\\)'\\. Supported form\\(s\\): 'ARRAY_EXCEPT\\(, "
+ "\\)'", false);
+ f.checkFails("^array_except(null, null)^",
+ "Cannot apply 'ARRAY_EXCEPT' to arguments of type 'ARRAY_EXCEPT\\(, "
+ + "\\)'\\. Supported form\\(s\\): 'ARRAY_EXCEPT\\(, "
+ + "\\)'", false);
}
/** Tests {@code ARRAY_INSERT} function from Spark. */
@@ -6906,6 +6903,10 @@ void checkRegexpExtract(SqlOperatorFixture f0, FunctionAlias functionAlias) {
"Cannot apply 'ARRAY_INTERSECT' to arguments of type 'ARRAY_INTERSECT\\(, "
+ "\\)'\\. Supported form\\(s\\): 'ARRAY_INTERSECT\\(, "
+ "\\)'", false);
+ f.checkFails("^array_intersect(null, null)^",
+ "Cannot apply 'ARRAY_INTERSECT' to arguments of type 'ARRAY_INTERSECT\\(, "
+ + "\\)'\\. Supported form\\(s\\): 'ARRAY_INTERSECT\\(, "
+ + "\\)'", false);
}
/** Tests {@code ARRAY_UNION} function from Spark. */