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

Reply via email to