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

xiong 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 e969f87aea [CALCITE-5778] Add ARRAY_JOIN, ARRAYS_OVERLAP, ARRAYS_ZIP 
function (enabled in Spark library)
e969f87aea is described below

commit e969f87aeac35ffd97b80a2ef39a0f749a645a8a
Author: yongen.ly <[email protected]>
AuthorDate: Wed Jun 14 16:27:31 2023 +0800

    [CALCITE-5778] Add ARRAY_JOIN, ARRAYS_OVERLAP, ARRAYS_ZIP function (enabled 
in Spark library)
---
 .../calcite/adapter/enumerable/RexImpTable.java    |   6 ++
 .../org/apache/calcite/runtime/SqlFunctions.java   |  50 ++++++++++
 .../main/java/org/apache/calcite/sql/SqlKind.java  |   9 ++
 .../calcite/sql/fun/SqlLibraryOperators.java       |  44 +++++++++
 .../apache/calcite/sql/type/SqlTypeTransforms.java |  21 ++++
 .../org/apache/calcite/util/BuiltInMethod.java     |   2 +
 .../org/apache/calcite/test/SqlFunctionsTest.java  |  30 ++++++
 site/_docs/reference.md                            |   3 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 109 +++++++++++++++++++++
 9 files changed, 274 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 f1585e4e30..06603af460 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
@@ -117,6 +117,8 @@ import static 
org.apache.calcite.sql.fun.SqlInternalOperators.LITERAL_AGG;
 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.ARRAYS_OVERLAP;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAYS_ZIP;
 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;
@@ -126,6 +128,7 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_CONTAINS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_DISTINCT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_EXCEPT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_INTERSECT;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_JOIN;
 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;
@@ -725,6 +728,7 @@ public class RexImpTable {
       defineMethod(ARRAY_CONTAINS, BuiltInMethod.LIST_CONTAINS.method, 
NullPolicy.ANY);
       defineMethod(ARRAY_DISTINCT, BuiltInMethod.ARRAY_DISTINCT.method, 
NullPolicy.STRICT);
       defineMethod(ARRAY_EXCEPT, BuiltInMethod.ARRAY_EXCEPT.method, 
NullPolicy.ANY);
+      defineMethod(ARRAY_JOIN, "arrayToString", NullPolicy.STRICT);
       defineMethod(ARRAY_INTERSECT, BuiltInMethod.ARRAY_INTERSECT.method, 
NullPolicy.ANY);
       defineMethod(ARRAY_LENGTH, BuiltInMethod.COLLECTION_SIZE.method, 
NullPolicy.STRICT);
       defineMethod(ARRAY_MAX, BuiltInMethod.ARRAY_MAX.method, 
NullPolicy.STRICT);
@@ -737,6 +741,8 @@ public class RexImpTable {
       defineMethod(ARRAY_SIZE, BuiltInMethod.COLLECTION_SIZE.method, 
NullPolicy.STRICT);
       defineMethod(ARRAY_TO_STRING, "arrayToString", NullPolicy.STRICT);
       defineMethod(ARRAY_UNION, BuiltInMethod.ARRAY_UNION.method, 
NullPolicy.ANY);
+      defineMethod(ARRAYS_OVERLAP, BuiltInMethod.ARRAYS_OVERLAP.method, 
NullPolicy.ANY);
+      defineMethod(ARRAYS_ZIP, BuiltInMethod.ARRAYS_ZIP.method, 
NullPolicy.ANY);
       defineMethod(MAP_ENTRIES, BuiltInMethod.MAP_ENTRIES.method, 
NullPolicy.STRICT);
       defineMethod(MAP_KEYS, BuiltInMethod.MAP_KEYS.method, NullPolicy.STRICT);
       defineMethod(MAP_VALUES, BuiltInMethod.MAP_VALUES.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 0638481ae2..486153d423 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -3913,6 +3913,56 @@ public class SqlFunctions {
     return atomic;
   }
 
+  /** Support the ARRAYS_OVERLAP function. */
+  public static @Nullable Boolean arraysOverlap(List list1, List list2) {
+    if (list1.size() > list2.size()) {
+      return arraysOverlap(list2, list1);
+    }
+    final List smaller = list1;
+    final List bigger = list2;
+    boolean hasNull = false;
+    if (smaller.size() > 0 && bigger.size() > 0) {
+      final Set smallestSet = new HashSet(smaller);
+      hasNull = smallestSet.remove(null);
+      for (Object element : bigger) {
+        if (element == null) {
+          hasNull = true;
+        } else if (smallestSet.contains(element)) {
+          return true;
+        }
+      }
+    }
+    if (hasNull) {
+      return null;
+    } else {
+      return false;
+    }
+  }
+
+  /** Support the ARRAYS_ZIP function. */
+  @SuppressWarnings("argument.type.incompatible")
+  public static List arraysZip(List... lists) {
+    final int biggestCardinality = lists.length == 0
+        ? 0
+        : Arrays.stream(lists).mapToInt(List::size).max().getAsInt();
+
+    final List result = new ArrayList(biggestCardinality);
+    for (int i = 0; i < biggestCardinality; i++) {
+      List<Object> row = new ArrayList<>();
+      Object value;
+      for (List list : lists) {
+        if (i < list.size() && list.get(i) != null) {
+          value = list.get(i);
+        } else {
+          value = null;
+        }
+        row.add(value);
+      }
+      result.add(row);
+    }
+    return result;
+  }
+
   /** Support the ARRAY_COMPACT function. */
   public static List compact(List list) {
     final List result = new ArrayList();
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 ebeeb7f839..001c3fdea0 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -707,6 +707,9 @@ public enum SqlKind {
   /** {@code ARRAY_INTERSECT} function (Spark semantics). */
   ARRAY_INTERSECT,
 
+  /** {@code ARRAY_JOIN} function (Spark semantics). */
+  ARRAY_JOIN,
+
   /** {@code ARRAY_LENGTH} function (Spark semantics). */
   ARRAY_LENGTH,
 
@@ -740,6 +743,12 @@ public enum SqlKind {
   /** {@code ARRAY_UNION} function (Spark semantics). */
   ARRAY_UNION,
 
+  /** {@code ARRAYS_OVERLAP} function (Spark semantics). */
+  ARRAYS_OVERLAP,
+
+  /** {@code ARRAYS_ZIP} function (Spark semantics). */
+  ARRAYS_ZIP,
+
   /** {@code SORT_ARRAY} function (Spark semantics). */
   SORT_ARRAY,
 
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 d42beb1ec5..07b150105a 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
@@ -53,6 +53,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 import static org.apache.calcite.sql.fun.SqlLibrary.ALL;
 import static org.apache.calcite.sql.fun.SqlLibrary.BIG_QUERY;
@@ -1037,6 +1039,13 @@ public abstract class SqlLibraryOperators {
               OperandTypes.SAME_SAME,
               OperandTypes.family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY)));
 
+  /** The "ARRAY_JOIN(array, delimiter [, nullText ])" function. */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction ARRAY_JOIN =
+      SqlBasicFunction.create(SqlKind.ARRAY_JOIN,
+          ReturnTypes.VARCHAR_NULLABLE,
+          OperandTypes.STRING_ARRAY_CHARACTER_OPTIONAL_CHARACTER);
+
   /** The "ARRAY_LENGTH(array)" function. */
   @LibraryOperator(libraries = {BIG_QUERY})
   public static final SqlFunction ARRAY_LENGTH =
@@ -1118,6 +1127,41 @@ public abstract class SqlLibraryOperators {
           ReturnTypes.VARCHAR_NULLABLE,
           OperandTypes.STRING_ARRAY_CHARACTER_OPTIONAL_CHARACTER);
 
+  /** The "ARRAYS_OVERLAP(array1, array2)" function (Spark). */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction ARRAYS_OVERLAP =
+      SqlBasicFunction.create(SqlKind.ARRAYS_OVERLAP,
+          
ReturnTypes.BOOLEAN_NULLABLE.andThen(SqlTypeTransforms.COLLECTION_ELEMENT_TYPE_NULLABLE),
+          OperandTypes.and(
+              OperandTypes.SAME_SAME,
+              OperandTypes.family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY)));
+
+  private static RelDataType deriveTypeArraysZip(SqlOperatorBinding opBinding) 
{
+    final List<RelDataType> argComponentTypes = new ArrayList<>();
+    for (RelDataType arrayType : opBinding.collectOperandTypes()) {
+      final RelDataType componentType = 
requireNonNull(arrayType.getComponentType());
+      argComponentTypes.add(componentType);
+    }
+
+    final List<String> indexes = IntStream.range(0, argComponentTypes.size())
+        .mapToObj(i -> String.valueOf(i))
+        .collect(Collectors.toList());
+    final RelDataType structType =
+        opBinding.getTypeFactory().createStructType(argComponentTypes, 
indexes);
+    return SqlTypeUtil.createArrayType(
+        opBinding.getTypeFactory(),
+        requireNonNull(structType, "inferred value type"),
+        false);
+  }
+
+  /** The "ARRAYS_ZIP(array, ...)" function (Spark). */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction ARRAYS_ZIP =
+      SqlBasicFunction.create(SqlKind.ARRAYS_ZIP,
+          ((SqlReturnTypeInference) SqlLibraryOperators::deriveTypeArraysZip)
+              .andThen(SqlTypeTransforms.TO_NULLABLE),
+          OperandTypes.SAME_VARIADIC);
+
   /** The "SORT_ARRAY(array)" function (Spark). */
   @LibraryOperator(libraries = {SPARK})
   public static final SqlFunction SORT_ARRAY =
diff --git 
a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java 
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
index b9c6f80aa1..3676653cce 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeTransforms.java
@@ -22,6 +22,7 @@ import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.sql.SqlOperatorBinding;
 import org.apache.calcite.util.Util;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import static org.apache.calcite.sql.type.NonNullableAccessors.getCharset;
@@ -111,6 +112,26 @@ public abstract class SqlTypeTransforms {
         return typeToTransform;
       };
 
+  /**
+   * Parameter type-inference transform strategy where a derived type is
+   * transformed into the same type but nullable if any of element of a calls 
operands is
+   * nullable.
+   */
+  public static final SqlTypeTransform COLLECTION_ELEMENT_TYPE_NULLABLE =
+      (opBinding, typeToTransform) -> {
+        final List<RelDataType> argComponentTypes = new ArrayList<>();
+        for (RelDataType arrayType : opBinding.collectOperandTypes()) {
+          final RelDataType componentType = 
requireNonNull(arrayType.getComponentType());
+          argComponentTypes.add(componentType);
+        }
+
+        if (argComponentTypes.stream().anyMatch(RelDataType::isNullable)) {
+          return opBinding.getTypeFactory()
+              .createTypeWithNullability(typeToTransform, true);
+        }
+        return typeToTransform;
+      };
+
   /**
    * Type-inference strategy whereby the result type of a call is VARYING the
    * type given. The length returned is the same as length of the first
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 64073b3977..969cc0daf8 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -651,6 +651,8 @@ public enum BuiltInMethod {
   ARRAY_INTERSECT(SqlFunctions.class, "arrayIntersect", List.class, 
List.class),
   ARRAY_UNION(SqlFunctions.class, "arrayUnion", List.class, List.class),
   ARRAY_REVERSE(SqlFunctions.class, "reverse", List.class),
+  ARRAYS_OVERLAP(SqlFunctions.class, "arraysOverlap", List.class, List.class),
+  ARRAYS_ZIP(SqlFunctions.class, "arraysZip", List.class, List.class),
   SORT_ARRAY(SqlFunctions.class, "sortArray", List.class, boolean.class),
   MAP_ENTRIES(SqlFunctions.class, "mapEntries", Map.class),
   MAP_KEYS(SqlFunctions.class, "mapKeys", Map.class),
diff --git a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
index 2dbce65720..b973d8c2b8 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java
@@ -40,6 +40,7 @@ import static 
org.apache.calcite.avatica.util.DateTimeUtils.MILLIS_PER_DAY;
 import static 
org.apache.calcite.avatica.util.DateTimeUtils.dateStringToUnixDate;
 import static 
org.apache.calcite.avatica.util.DateTimeUtils.timeStringToUnixDate;
 import static 
org.apache.calcite.avatica.util.DateTimeUtils.timestampStringToUnixDate;
+import static org.apache.calcite.runtime.SqlFunctions.arraysOverlap;
 import static org.apache.calcite.runtime.SqlFunctions.charLength;
 import static org.apache.calcite.runtime.SqlFunctions.concat;
 import static org.apache.calcite.runtime.SqlFunctions.concatMulti;
@@ -99,6 +100,35 @@ class SqlFunctionsTest {
     return ImmutableList.of();
   }
 
+  @Test void testArraysOverlap() {
+    final List<Object> listWithOnlyNull = new ArrayList<>();
+    listWithOnlyNull.add(null);
+
+    // list2 is empty
+    assertThat(arraysOverlap(list(), list()), is(false));
+    assertThat(arraysOverlap(listWithOnlyNull, list()), is(false));
+    assertThat(arraysOverlap(list(1, null), list()), is(false));
+    assertThat(arraysOverlap(list(1, 2), list()), is(false));
+
+    // list2 contains only nulls
+    assertThat(arraysOverlap(list(), listWithOnlyNull), is(false));
+    assertThat(arraysOverlap(listWithOnlyNull, listWithOnlyNull), 
is(nullValue()));
+    assertThat(arraysOverlap(list(1, null), listWithOnlyNull), 
is(nullValue()));
+    assertThat(arraysOverlap(list(1, 2), listWithOnlyNull), is(nullValue()));
+
+    // list2 contains a mixture of nulls and non-nulls
+    assertThat(arraysOverlap(list(), list(1, null)), is(false));
+    assertThat(arraysOverlap(listWithOnlyNull, list(1, null)), 
is(nullValue()));
+    assertThat(arraysOverlap(list(1, null), list(1, null)), is(true));
+    assertThat(arraysOverlap(list(1, 2), list(1, null)), is(true));
+
+    // list2 contains only non-null
+    assertThat(arraysOverlap(list(), list(1, 2)), is(false));
+    assertThat(arraysOverlap(listWithOnlyNull, list(1, 2)), is(nullValue()));
+    assertThat(arraysOverlap(list(1, null), list(1, 2)), is(true));
+    assertThat(arraysOverlap(list(1, 2), list(1, 2)), is(true));
+  }
+
   @Test void testCharLength() {
     assertThat(charLength("xyz"), is(3));
   }
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index c45baae259..bd2e612aa3 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2659,6 +2659,7 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | s | ARRAY_DISTINCT(array)                          | Removes duplicate 
values from the *array* that keeps ordering of elements
 | s | ARRAY_EXCEPT(array1, array2)                   | Returns an array of the 
elements in *array1* but not in *array2*, without duplicates
 | s | ARRAY_INTERSECT(array1, array2)                | Returns an array of the 
elements in the intersection of *array1* and *array2*, without duplicates
+| s | ARRAY_JOIN(array, delimiter [, nullText ])     | Synonym for 
`ARRAY_TO_STRING`
 | 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*
@@ -2670,6 +2671,8 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | s | ARRAY_SIZE(array)                              | Synonym for 
`CARDINALITY`
 | b | ARRAY_TO_STRING(array, delimiter [, nullText ])| Returns a concatenation 
of the elements in *array* as a STRING and take *delimiter* as the delimiter. 
If the *nullText* parameter is used, the function replaces any `NULL` values in 
the array with the value of *nullText*. If the *nullText* parameter is not 
used, the function omits the `NULL` value and its preceding delimiter
 | s | ARRAY_UNION(array1, array2)                    | Returns an array of the 
elements in the union of *array1* and *array2*, without duplicates
+| s | ARRAYS_OVERLAP(array1, array2)                 | Returns true if *array1 
contains at least a non-null element present also in *array2*. If the arrays 
have no common element and they are both non-empty and either of them contains 
a null element null is returned, false otherwise
+| s | ARRAYS_ZIP(array [, array ]*)                  | Returns a merged 
*array* of structs in which the N-th struct contains all N-th values of input 
arrays
 | s | SORT_ARRAY(array [, ascendingOrder])           | Sorts the *array* in 
ascending or descending order according to the natural ordering of the array 
elements. The default order is ascending if *ascendingOrder* is not specified. 
Null elements will be placed at the beginning of the returned array in 
ascending order or at the end of the returned array in descending order
 | * | ASINH(numeric)                                 | Returns the inverse 
hyperbolic sine of *numeric*
 | * | ATANH(numeric)                                 | Returns the inverse 
hyperbolic tangent of *numeric*
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 cd11a0f8fb..424f626f99 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -5515,6 +5515,31 @@ public class SqlOperatorTest {
     f.checkNull("array_distinct(null)");
   }
 
+  @Test void testArrayJoinFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.ARRAY_JOIN);
+    f0.checkFails("^array_join(array['aa', 'b', 'c'], '-')^", "No match found 
for function"
+        + " signature ARRAY_JOIN\\(<CHAR\\(2\\) ARRAY>, <CHARACTER>\\)", 
false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("array_join(array['aa', 'b', 'c'], '-')", "aa-b-c",
+        "VARCHAR NOT NULL");
+    f.checkScalar("array_join(array[null, 'aa', null, 'b', null], '-', 
'empty')",
+        "empty-aa-empty-b-empty", "VARCHAR NOT NULL");
+    f.checkScalar("array_join(array[null, 'aa', null, 'b', null], '-')", 
"aa-b",
+        "VARCHAR NOT NULL");
+    f.checkScalar("array_join(array[null, x'aa', null, x'bb', null], '-')", 
"aa-bb",
+        "VARCHAR NOT NULL");
+    f.checkScalar("array_join(array['', 'b'], '-')", "-b", "VARCHAR NOT NULL");
+    f.checkScalar("array_join(array['', ''], '-')", "-", "VARCHAR NOT NULL");
+    f.checkNull("array_join(null, '-')");
+    f.checkNull("array_join(array['a', 'b', null], null)");
+    f.checkFails("^array_join(array[1, 2, 3], '-', ' ')^",
+        "Cannot apply 'ARRAY_JOIN' to arguments of type 'ARRAY_JOIN\\("
+            + "<INTEGER ARRAY>, <CHAR\\(1\\)>, <CHAR\\(1\\)>\\)'\\. Supported 
form\\(s\\):"
+            + " ARRAY_JOIN\\(<STRING ARRAY>, <CHARACTER>\\[, 
<CHARACTER>\\]\\)", false);
+  }
+
   /** Tests {@code ARRAY_MAX} function from Spark. */
   @Test void testArrayMaxFunc() {
     final SqlOperatorFixture f0 = fixture();
@@ -5791,6 +5816,90 @@ public class SqlOperatorTest {
     f.checkNull("array_union(cast(null as integer array), cast(null as integer 
array))");
   }
 
+  /** Tests {@code ARRAYS_OVERLAP} function from Spark. */
+  @Test void testArraysOverlapFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.ARRAYS_OVERLAP);
+    f0.checkFails("^arrays_overlap(array[1, 2], array[2])^",
+        "No match found for function signature ARRAYS_OVERLAP\\("
+            + "<INTEGER ARRAY>, <INTEGER ARRAY>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("arrays_overlap(array[1, 2], array[2])", true,
+        "BOOLEAN NOT NULL");
+    f.checkScalar("arrays_overlap(array[1, 2], array[3])", false,
+        "BOOLEAN NOT NULL");
+    f.checkScalar("arrays_overlap(array[1, null], array[1])", true,
+        "BOOLEAN");
+    f.checkScalar("arrays_overlap(array(), array(2))", false,
+        "BOOLEAN NOT NULL");
+    f.checkScalar("arrays_overlap(array(), array())", false,
+        "BOOLEAN NOT NULL");
+    f.checkScalar("arrays_overlap(array(), array(1, null))", false,
+        "BOOLEAN");
+    f.checkScalar("arrays_overlap(array[array[1, 2], array[3, 4]], 
array[array[1, 2]])", true,
+        "BOOLEAN NOT NULL");
+    f.checkScalar("arrays_overlap(array[map[1, 'a'], map[2, 'b']], 
array[map[1, 'a']])", true,
+        "BOOLEAN NOT NULL");
+    f.checkNull("arrays_overlap(cast(null as integer array), array[1, 2])");
+    f.checkType("arrays_overlap(cast(null as integer array), array[1, 2])", 
"BOOLEAN");
+    f.checkNull("arrays_overlap(array[1, 2], cast(null as integer array))");
+    f.checkType("arrays_overlap(array[1, 2], cast(null as integer array))", 
"BOOLEAN");
+    f.checkNull("arrays_overlap(array[1], array[2, null])");
+    f.checkType("arrays_overlap(array[2, null], array[1])", "BOOLEAN");
+    f.checkFails("^arrays_overlap(array[1, 2], true)^",
+        "Cannot apply 'ARRAYS_OVERLAP' to arguments of type 'ARRAYS_OVERLAP\\("
+            + "<INTEGER ARRAY>, <BOOLEAN>\\)'. Supported form\\(s\\): 
'ARRAYS_OVERLAP\\("
+            + "<EQUIVALENT_TYPE>, <EQUIVALENT_TYPE>\\)'", false);
+  }
+
+  /** Tests {@code ARRAYS_ZIP} function from Spark. */
+  @Test void testArraysZipFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.ARRAYS_ZIP);
+    f0.checkFails("^arrays_zip(array[1, 2], array[2])^",
+        "No match found for function signature ARRAYS_ZIP\\("
+            + "<INTEGER ARRAY>, <INTEGER ARRAY>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("arrays_zip(array[1, 2], array[2, 3], array[3, 4])", "[{1, 
2, 3}, {2, 3, 4}]",
+        "RecordType(INTEGER NOT NULL 0, INTEGER NOT NULL 1, INTEGER NOT NULL 
2) "
+            + "NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array[1, 2], array[2])", "[{1, 2}, {2, null}]",
+        "RecordType(INTEGER NOT NULL 0, INTEGER NOT NULL 1) NOT NULL ARRAY NOT 
NULL");
+    f.checkScalar("arrays_zip(array[1], array[2, null])", "[{1, 2}, {null, 
null}]",
+        "RecordType(INTEGER NOT NULL 0, INTEGER 1) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array[1, 2])", "[{1}, {2}]",
+        "RecordType(INTEGER NOT NULL 0) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array(), array(1, 2))", "[{null, 1}, {null, 2}]",
+        "RecordType(UNKNOWN NOT NULL 0, INTEGER NOT NULL 1) NOT NULL ARRAY NOT 
NULL");
+    f.checkScalar("arrays_zip(array(), array())", "[]",
+        "RecordType(UNKNOWN NOT NULL 0, UNKNOWN NOT NULL 1) NOT NULL ARRAY NOT 
NULL");
+    f.checkScalar("arrays_zip(array())", "[]",
+        "RecordType(UNKNOWN NOT NULL 0) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip()", "[]",
+        "RecordType() NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array(null), array(1))", "[{null, 1}]",
+        "RecordType(NULL 0, INTEGER NOT NULL 1) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array[array[1, 2], array[3, 4]], array[array[1, 
2]])",
+        "[{[1, 2], [1, 2]}, {[3, 4], null}]",
+        "RecordType(INTEGER NOT NULL ARRAY NOT NULL 0, INTEGER NOT NULL ARRAY 
NOT NULL 1) "
+            + "NOT NULL ARRAY NOT NULL");
+    f.checkScalar("arrays_zip(array[map[1, 'a'], map[2, 'b']], array[map[1, 
'a']])",
+        "[{{1=a}, {1=a}}, {{2=b}, null}]",
+        "RecordType((INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL 0, "
+            + "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL 1) NOT NULL 
ARRAY NOT NULL");
+
+    f.checkNull("arrays_zip(cast(null as integer array), array[1, 2])");
+    f.checkType("arrays_zip(cast(null as integer array), array[1, 2])",
+        "RecordType(INTEGER NOT NULL 0, INTEGER NOT NULL 1) NOT NULL ARRAY");
+    f.checkNull("arrays_zip(array[1, 2], cast(null as integer array))");
+    f.checkType("arrays_zip(array[1, 2], cast(null as integer array))",
+        "RecordType(INTEGER NOT NULL 0, INTEGER NOT NULL 1) NOT NULL ARRAY");
+    f.checkFails("^arrays_zip(array[1, 2], true)^",
+        "Parameters must be of the same type", false);
+  }
+
   /** Tests {@code SORT_ARRAY} function from Spark. */
   @Test void testSortArrayFunc() {
     final SqlOperatorFixture f0 = fixture();

Reply via email to