This is an automated email from the ASF dual-hosted git repository. jiajunxie 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 a2d2a31a70 [CALCITE-5751] Add ARRAY_APPEND, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_PREPEND function (enabled in Spark library) a2d2a31a70 is described below commit a2d2a31a70be3b20f3f2b8f311bf580dd9ae1e24 Author: yongen.ly <yongen...@alibaba-inc.com> AuthorDate: Sun Jun 11 10:23:19 2023 +0800 [CALCITE-5751] Add ARRAY_APPEND, ARRAY_POSITION, ARRAY_REMOVE, ARRAY_PREPEND function (enabled in Spark library) --- .../calcite/adapter/enumerable/RexImpTable.java | 8 ++ .../org/apache/calcite/runtime/SqlFunctions.java | 36 ++++++ .../main/java/org/apache/calcite/sql/SqlKind.java | 12 ++ .../calcite/sql/fun/SqlLibraryOperators.java | 45 ++++++++ .../org/apache/calcite/sql/type/ReturnTypes.java | 8 ++ .../org/apache/calcite/util/BuiltInMethod.java | 4 + site/_docs/reference.md | 4 + .../org/apache/calcite/test/SqlOperatorTest.java | 126 +++++++++++++++++++++ 8 files changed, 243 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index 1144b140fd..3a27c361b1 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -118,6 +118,7 @@ import static org.apache.calcite.sql.fun.SqlInternalOperators.THROW_UNLESS; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ACOSH; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_AGG; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_APPEND; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_COMPACT; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_CONCAT; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_CONCAT_AGG; @@ -128,6 +129,9 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_INTERSECT; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_LENGTH; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_MAX; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_MIN; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_POSITION; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_PREPEND; +import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_REMOVE; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_REPEAT; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_REVERSE; import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_SIZE; @@ -698,6 +702,7 @@ public class RexImpTable { defineMethod(ELEMENT, BuiltInMethod.ELEMENT.method, NullPolicy.STRICT); defineMethod(STRUCT_ACCESS, BuiltInMethod.STRUCT_ACCESS.method, NullPolicy.ANY); defineMethod(MEMBER_OF, BuiltInMethod.MEMBER_OF.method, NullPolicy.NONE); + defineMethod(ARRAY_APPEND, BuiltInMethod.ARRAY_APPEND.method, NullPolicy.ARG0); defineMethod(ARRAY_COMPACT, BuiltInMethod.ARRAY_COMPACT.method, NullPolicy.STRICT); defineMethod(ARRAY_CONTAINS, BuiltInMethod.LIST_CONTAINS.method, NullPolicy.ANY); defineMethod(ARRAY_DISTINCT, BuiltInMethod.ARRAY_DISTINCT.method, NullPolicy.STRICT); @@ -706,6 +711,9 @@ public class RexImpTable { defineMethod(ARRAY_LENGTH, BuiltInMethod.COLLECTION_SIZE.method, NullPolicy.STRICT); defineMethod(ARRAY_MAX, BuiltInMethod.ARRAY_MAX.method, NullPolicy.STRICT); defineMethod(ARRAY_MIN, BuiltInMethod.ARRAY_MIN.method, NullPolicy.STRICT); + defineMethod(ARRAY_PREPEND, BuiltInMethod.ARRAY_PREPEND.method, NullPolicy.ARG0); + defineMethod(ARRAY_POSITION, BuiltInMethod.ARRAY_POSITION.method, NullPolicy.ANY); + defineMethod(ARRAY_REMOVE, BuiltInMethod.ARRAY_REMOVE.method, NullPolicy.ANY); defineMethod(ARRAY_REPEAT, BuiltInMethod.ARRAY_REPEAT.method, NullPolicy.NONE); defineMethod(ARRAY_REVERSE, BuiltInMethod.ARRAY_REVERSE.method, NullPolicy.STRICT); defineMethod(ARRAY_SIZE, BuiltInMethod.COLLECTION_SIZE.method, NullPolicy.STRICT); diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java index 6157af6061..e7473b4790 100644 --- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java +++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java @@ -3904,6 +3904,14 @@ public class SqlFunctions { return result; } + /** Support the ARRAY_APPEND function. */ + public static List arrayAppend(List list, Object element) { + final List result = new ArrayList(list.size() + 1); + result.addAll(list); + result.add(element); + return result; + } + /** Support the ARRAY_DISTINCT function. * * <p>Note: If the list does not contain null, @@ -3941,6 +3949,34 @@ public class SqlFunctions { return min; } + /** Support the ARRAY_PREPEND function. */ + public static List arrayPrepend(List list, Object element) { + final List result = new ArrayList(list.size() + 1); + result.add(element); + result.addAll(list); + return result; + } + + /** Support the ARRAY_POSITION function. */ + public static Long arrayPosition(List list, Object element) { + final int index = list.indexOf(element); + if (index != -1) { + return Long.valueOf(index + 1L); + } + return 0L; + } + + /** Support the ARRAY_REMOVE function. */ + public static List arrayRemove(List list, Object element) { + final List result = new ArrayList(); + for (Object obj : list) { + if (obj == null || !obj.equals(element)) { + result.add(obj); + } + } + return result; + } + /** Support the ARRAY_REPEAT function. */ public static @Nullable List<Object> repeat(Object element, Object count) { if (count == null) { diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java index 06d4271e4a..ed48b413bf 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java @@ -683,6 +683,9 @@ public enum SqlKind { /** {@code EXTRACT} function. */ EXTRACT, + /** {@code ARRAY_APPEND} function (Spark semantics). */ + ARRAY_APPEND, + /** {@code ARRAY_COMPACT} function (Spark semantics). */ ARRAY_COMPACT, @@ -710,6 +713,15 @@ public enum SqlKind { /** {@code ARRAY_MIN} function (Spark semantics). */ ARRAY_MIN, + /** {@code ARRAY_POSITION} function (Spark semantics). */ + ARRAY_POSITION, + + /** {@code ARRAY_PREPEND} function (Spark semantics). */ + ARRAY_PREPEND, + + /** {@code ARRAY_REMOVE} function (Spark semantics). */ + ARRAY_REMOVE, + /** {@code ARRAY_REPEAT} function (Spark semantics). */ ARRAY_REPEAT, 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 71f5f1541a..ebcbf20e7f 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 @@ -47,6 +47,8 @@ import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Optionality; import org.apache.calcite.util.Static; +import com.google.common.collect.ImmutableList; + import org.checkerframework.checker.nullness.qual.Nullable; import java.util.ArrayList; @@ -907,6 +909,28 @@ public abstract class SqlLibraryOperators { SqlLibraryOperators::arrayReturnType, OperandTypes.SAME_VARIADIC); + @SuppressWarnings("argument.type.incompatible") + private static RelDataType arrayAppendPrependReturnType(SqlOperatorBinding opBinding) { + final RelDataType arrayType = opBinding.collectOperandTypes().get(0); + final RelDataType componentType = arrayType.getComponentType(); + final RelDataType elementType = opBinding.collectOperandTypes().get(1); + RelDataType type = + opBinding.getTypeFactory().leastRestrictive( + ImmutableList.of(componentType, elementType)); + if (elementType.isNullable()) { + type = opBinding.getTypeFactory().createTypeWithNullability(type, true); + } + requireNonNull(type, "inferred array element type"); + return SqlTypeUtil.createArrayType(opBinding.getTypeFactory(), type, arrayType.isNullable()); + } + + /** The "ARRAY_APPEND(array, element)" function. */ + @LibraryOperator(libraries = {SPARK}) + public static final SqlFunction ARRAY_APPEND = + SqlBasicFunction.create(SqlKind.ARRAY_APPEND, + SqlLibraryOperators::arrayAppendPrependReturnType, + OperandTypes.ARRAY_ELEMENT); + /** The "ARRAY_COMPACT(array)" function. */ @LibraryOperator(libraries = {SPARK}) public static final SqlFunction ARRAY_COMPACT = @@ -974,6 +998,27 @@ public abstract class SqlLibraryOperators { ReturnTypes.TO_COLLECTION_ELEMENT_FORCE_NULLABLE, OperandTypes.ARRAY); + /** The "ARRAY_POSITION(array, element)" function. */ + @LibraryOperator(libraries = {SPARK}) + public static final SqlFunction ARRAY_POSITION = + SqlBasicFunction.create(SqlKind.ARRAY_POSITION, + ReturnTypes.BIGINT_NULLABLE, + OperandTypes.ARRAY_ELEMENT); + + /** The "ARRAY_PREPEND(array, element)" function. */ + @LibraryOperator(libraries = {SPARK}) + public static final SqlFunction ARRAY_PREPEND = + SqlBasicFunction.create(SqlKind.ARRAY_PREPEND, + SqlLibraryOperators::arrayAppendPrependReturnType, + OperandTypes.ARRAY_ELEMENT); + + /** The "ARRAY_REMOVE(array, element)" function. */ + @LibraryOperator(libraries = {SPARK}) + public static final SqlFunction ARRAY_REMOVE = + SqlBasicFunction.create(SqlKind.ARRAY_REMOVE, + ReturnTypes.ARG0_NULLABLE, + OperandTypes.ARRAY_ELEMENT); + /** The "ARRAY_REPEAT(element, count)" function. */ @LibraryOperator(libraries = {SPARK}) public static final SqlFunction ARRAY_REPEAT = diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java index 6699bd8fcf..da77594623 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java @@ -177,6 +177,14 @@ public abstract class ReturnTypes { public static final SqlReturnTypeInference ARG0_NULLABLE = ARG0.andThen(SqlTypeTransforms.TO_NULLABLE); + /** + * Type-inference strategy whereby the result type of a call is the type of + * the operand #0 (0-based). If the operand #0 (0-based) is nullable, the + * returned type will also be nullable. + */ + public static final SqlReturnTypeInference ARG0_NULLABLE_IF_ARG0_NULLABLE = + ARG0.andThen(SqlTypeTransforms.ARG0_NULLABLE); + /** * Type-inference strategy whereby the result type of a call is the type of * the operand #0 (0-based), with nulls always allowed. diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java index e74bc6d553..4e50e002f8 100644 --- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java +++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java @@ -631,10 +631,14 @@ public enum BuiltInMethod { IS_EMPTY(Collection.class, "isEmpty"), SUBMULTISET_OF(SqlFunctions.class, "submultisetOf", Collection.class, Collection.class), + ARRAY_APPEND(SqlFunctions.class, "arrayAppend", List.class, Object.class), ARRAY_COMPACT(SqlFunctions.class, "compact", List.class), ARRAY_DISTINCT(SqlFunctions.class, "distinct", List.class), ARRAY_MAX(SqlFunctions.class, "arrayMax", List.class), ARRAY_MIN(SqlFunctions.class, "arrayMin", List.class), + ARRAY_POSITION(SqlFunctions.class, "arrayPosition", List.class, Object.class), + ARRAY_PREPEND(SqlFunctions.class, "arrayPrepend", List.class, Object.class), + ARRAY_REMOVE(SqlFunctions.class, "arrayRemove", List.class, Object.class), ARRAY_REPEAT(SqlFunctions.class, "repeat", Object.class, Integer.class), ARRAY_EXCEPT(SqlFunctions.class, "arrayExcept", List.class, List.class), ARRAY_INTERSECT(SqlFunctions.class, "arrayIntersect", List.class, List.class), diff --git a/site/_docs/reference.md b/site/_docs/reference.md index fb2e94cbf5..351cbb8b23 100644 --- a/site/_docs/reference.md +++ b/site/_docs/reference.md @@ -2652,6 +2652,7 @@ BigQuery's type system uses confusingly different names for types and functions: | m | expr1 <=> expr2 | Whether two values are equal, treating null values as the same, and it's similar to `IS NOT DISTINCT FROM` | * | ACOSH(numeric) | Returns the inverse hyperbolic cosine of *numeric* | s | ARRAY(expr [, expr ]*) | Construct an array in Apache Spark +| s | ARRAY_APPEND(array, element) | Appends an *element* to the end of the *array* and returns the result. Type of *element* should be similar to type of the elements of the *array*. If the *array* is null, the function will return null. If an *element* that is null, the null *element* will be added to the end of the *array* | s | ARRAY_COMPACT(array) | Removes null values from the *array* | b | ARRAY_CONCAT(array [, array ]*) | Concatenates one or more arrays. If any input argument is `NULL` the function returns `NULL` | s | ARRAY_CONTAINS(array, element) | Returns true if the *array* contains the *element* @@ -2661,6 +2662,9 @@ BigQuery's type system uses confusingly different names for types and functions: | b | ARRAY_LENGTH(array) | Synonym for `CARDINALITY` | s | ARRAY_MAX(array) | Returns the maximum value in the *array* | s | ARRAY_MIN(array) | Returns the minimum value in the *array* +| s | ARRAY_POSITION(array, element) | Returns the (1-based) index of the first *element* of the *array* as long +| s | ARRAY_REMOVE(array, element) | Remove all elements that equal to *element* from the *array* +| s | ARRAY_PREPEND(array, element) | Appends an *element* to the beginning of the *array* and returns the result. Type of *element* should be similar to type of the elements of the *array*. If the *array* is null, the function will return null. If an *element* that is null, the null *element* will be added to the beginning of the *array* | s | ARRAY_REPEAT(element, count) | Returns the array containing element count times. | b | ARRAY_REVERSE(array) | Reverses elements of *array* | s | ARRAY_SIZE(array) | Synonym for `CARDINALITY` 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 49c7d30c03..77d9734afe 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -5353,6 +5353,35 @@ public class SqlOperatorTest { f.checkScalar("rand_integer(2, 11)", 1, "INTEGER NOT NULL"); } + /** Tests {@code ARRAY_APPEND} function from Spark. */ + @Test void testArrayAppendFunc() { + final SqlOperatorFixture f0 = fixture(); + f0.setFor(SqlLibraryOperators.ARRAY_APPEND); + f0.checkFails("^array_append(array[1], 2)^", + "No match found for function signature ARRAY_APPEND\\(" + + "<INTEGER ARRAY>, <NUMERIC>\\)", false); + + final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK); + f.checkScalar("array_append(array[1], 2)", "[1, 2]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_append(array[1], null)", "[1, null]", + "INTEGER ARRAY NOT NULL"); + f.checkScalar("array_append(array(null), null)", "[null, null]", + "NULL ARRAY NOT NULL"); + f.checkScalar("array_append(array(), null)", "[null]", + "UNKNOWN ARRAY NOT NULL"); + f.checkScalar("array_append(array(), 1)", "[1]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_append(array[array[1, 2]], array[3, 4])", "[[1, 2], [3, 4]]", + "INTEGER NOT NULL ARRAY NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_append(array[map[1, 'a']], map[2, 'b'])", "[{1=a}, {2=b}]", + "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL ARRAY NOT NULL"); + f.checkNull("array_append(cast(null as integer array), 1)"); + f.checkType("array_append(cast(null as integer array), 1)", "INTEGER NOT NULL ARRAY"); + f.checkFails("^array_append(array[1, 2], true)^", + "INTEGER is not comparable to BOOLEAN", false); + } + /** Tests {@code ARRAY_COMPACT} function from Spark. */ @Test void testArrayCompactFunc() { final SqlOperatorFixture f0 = fixture(); @@ -5465,6 +5494,103 @@ public class SqlOperatorTest { f.checkNull("array_min(cast(null as integer array))"); } + /** Tests {@code ARRAY_POSITION} function from Spark. */ + @Test void testArrayPositionFunc() { + final SqlOperatorFixture f0 = fixture(); + f0.setFor(SqlLibraryOperators.ARRAY_POSITION); + f0.checkFails("^array_position(array[1], 1)^", + "No match found for function signature ARRAY_POSITION\\(" + + "<INTEGER ARRAY>, <NUMERIC>\\)", false); + + final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK); + f.checkScalar("array_position(array[1], 1)", "1", + "BIGINT NOT NULL"); + f.checkScalar("array_position(array[1, 2, 2], 2)", "2", + "BIGINT NOT NULL"); + f.checkScalar("array_position(array[1], 2)", "0", + "BIGINT NOT NULL"); + f.checkScalar("array_position(array(), 1)", "0", + "BIGINT NOT NULL"); + f.checkScalar("array_position(array[array[1, 2]], array[1, 2])", "1", + "BIGINT NOT NULL"); + f.checkScalar("array_position(array[map[1, 'a']], map[1, 'a'])", "1", + "BIGINT NOT NULL"); + f.checkNull("array_position(cast(null as integer array), 1)"); + f.checkType("array_position(cast(null as integer array), 1)", "BIGINT"); + f.checkNull("array_position(array[1], null)"); + f.checkType("array_position(array[1], null)", "BIGINT"); + f.checkFails("^array_position(array[1, 2], true)^", + "INTEGER is not comparable to BOOLEAN", false); + } + + /** Tests {@code ARRAY_PREPEND} function from Spark. */ + @Test void testArrayPrependFunc() { + final SqlOperatorFixture f0 = fixture(); + f0.setFor(SqlLibraryOperators.ARRAY_PREPEND); + f0.checkFails("^array_prepend(array[1], 2)^", + "No match found for function signature ARRAY_PREPEND\\(" + + "<INTEGER ARRAY>, <NUMERIC>\\)", false); + + final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK); + f.checkScalar("array_prepend(array[1], 2)", "[2, 1]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_prepend(array[1], null)", "[null, 1]", + "INTEGER ARRAY NOT NULL"); + f.checkScalar("array_prepend(array(null), null)", "[null, null]", + "NULL ARRAY NOT NULL"); + f.checkScalar("array_prepend(array(), null)", "[null]", + "UNKNOWN ARRAY NOT NULL"); + f.checkScalar("array_append(array(), 1)", "[1]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_prepend(array[array[1, 2]], array[3, 4])", "[[3, 4], [1, 2]]", + "INTEGER NOT NULL ARRAY NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_prepend(array[map[1, 'a']], map[2, 'b'])", "[{2=b}, {1=a}]", + "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL ARRAY NOT NULL"); + f.checkNull("array_prepend(cast(null as integer array), 1)"); + f.checkType("array_prepend(cast(null as integer array), 1)", "INTEGER NOT NULL ARRAY"); + f.checkFails("^array_prepend(array[1, 2], true)^", + "INTEGER is not comparable to BOOLEAN", false); + } + + /** Tests {@code ARRAY_REMOVE} function from Spark. */ + @Test void testArrayRemoveFunc() { + final SqlOperatorFixture f0 = fixture(); + f0.setFor(SqlLibraryOperators.ARRAY_REMOVE); + f0.checkFails("^array_remove(array[1], 1)^", + "No match found for function signature ARRAY_REMOVE\\(" + + "<INTEGER ARRAY>, <NUMERIC>\\)", false); + + final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK); + f.checkScalar("array_remove(array[1], 1)", "[]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_remove(array[1, 2, 1], 1)", "[2]", + "INTEGER NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_remove(array[1, 2, null], 1)", "[2, null]", + "INTEGER ARRAY NOT NULL"); + f.checkScalar("array_remove(array[1, 2, null], 3)", "[1, 2, null]", + "INTEGER ARRAY NOT NULL"); + f.checkScalar("array_remove(array(null), 1)", "[null]", + "NULL ARRAY NOT NULL"); + f.checkScalar("array_remove(array(), 1)", "[]", + "UNKNOWN NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_remove(array[array[1, 2]], array[1, 2])", "[]", + "INTEGER NOT NULL ARRAY NOT NULL ARRAY NOT NULL"); + f.checkScalar("array_remove(array[map[1, 'a']], map[1, 'a'])", "[]", + "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL ARRAY NOT NULL"); + f.checkNull("array_remove(cast(null as integer array), 1)"); + f.checkType("array_remove(cast(null as integer array), 1)", "INTEGER NOT NULL ARRAY"); + + // Flink and Spark differ on the following. The expression + // array_remove(array[1, null], cast(null as integer)) + // returns [1] in Flink, and returns null in Spark. The current + // function has Spark behavior, but if we supported a Flink function + // library (i.e. "fun=flink") we could add a function with Flink behavior. + f.checkNull("array_remove(array[1, null], cast(null as integer))"); + f.checkType("array_remove(array[1, null], cast(null as integer))", "INTEGER ARRAY"); + f.checkFails("^array_remove(array[1, 2], true)^", + "INTEGER is not comparable to BOOLEAN", false); + } + /** Tests {@code ARRAY_REPEAT} function from Spark. */ @Test void testArrayRepeatFunc() { final SqlOperatorFixture f0 = fixture();