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

jackylau 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 313b958059 [CALCITE-5772] Add MAP_CONCAT, MAP_FROM_ENTRIES function 
(enabled in Spark library)
313b958059 is described below

commit 313b9580591f3816e498feb26a8953602c6029c0
Author: yongen.ly <[email protected]>
AuthorDate: Tue Jun 13 13:16:59 2023 +0800

    [CALCITE-5772] Add MAP_CONCAT, MAP_FROM_ENTRIES function (enabled in Spark 
library)
---
 .../calcite/adapter/enumerable/RexImpTable.java    |  4 +
 .../apache/calcite/runtime/CalciteResource.java    |  3 +
 .../org/apache/calcite/runtime/SqlFunctions.java   | 19 +++++
 .../main/java/org/apache/calcite/sql/SqlKind.java  |  6 ++
 .../calcite/sql/fun/SqlLibraryOperators.java       | 46 +++++++++++
 .../org/apache/calcite/sql/type/OperandTypes.java  | 29 +++++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  2 +
 .../calcite/runtime/CalciteResource.properties     |  1 +
 site/_docs/reference.md                            |  6 +-
 .../org/apache/calcite/test/SqlOperatorTest.java   | 91 ++++++++++++++++++++++
 10 files changed, 205 insertions(+), 2 deletions(-)

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 6a90116e42..f709aae70e 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
@@ -192,8 +192,10 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.LOG;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_AND;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.LOGICAL_OR;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.LPAD;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_CONCAT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_ENTRIES;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_FROM_ARRAYS;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_FROM_ENTRIES;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_KEYS;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_VALUES;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAX_BY;
@@ -747,10 +749,12 @@ public class RexImpTable {
       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_CONCAT, BuiltInMethod.MAP_CONCAT.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);
       defineMethod(MAP_FROM_ARRAYS, BuiltInMethod.MAP_FROM_ARRAYS.method, 
NullPolicy.ANY);
+      defineMethod(MAP_FROM_ENTRIES, BuiltInMethod.MAP_FROM_ENTRIES.method, 
NullPolicy.STRICT);
       map.put(STR_TO_MAP, new StringToMapImplementor());
       map.put(ARRAY_CONCAT, new ArrayConcatImplementor());
       map.put(SORT_ARRAY, new SortArrayImplementor());
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 27024331bc..1b267bc598 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -702,6 +702,9 @@ public interface CalciteResource {
   @BaseMessage("Map requires an even number of arguments")
   ExInst<SqlValidatorException> mapRequiresEvenArgCount();
 
+  @BaseMessage("Function ''{0}'' should all be of type map, but it is ''{1}''")
+  ExInst<SqlValidatorException> typesShouldAllBeMap(String funcName, String 
type);
+
   @BaseMessage("Incompatible types")
   ExInst<SqlValidatorException> incompatibleTypes();
 
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 4f2e0177e1..7338cbfa52 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -4112,6 +4112,13 @@ public class SqlFunctions {
     return list;
   }
 
+  /** Support the MAP_CONCAT function. */
+  public static Map mapConcat(Map... maps) {
+    final Map result = new LinkedHashMap();
+    Arrays.stream(maps).forEach(result::putAll);
+    return result;
+  }
+
   /** Support the MAP_ENTRIES function. */
   public static List mapEntries(Map<Object, Object> map) {
     final List result = new ArrayList(map.size());
@@ -4143,6 +4150,18 @@ public class SqlFunctions {
     return map;
   }
 
+  /** Support the MAP_FROM_ENTRIES function. */
+  public static @Nullable Map mapFromEntries(List entries) {
+    final Map map = new LinkedHashMap<>();
+    for (Object entry: entries) {
+      if (entry == null) {
+        return null;
+      }
+      map.put(structAccess(entry, 0, null), structAccess(entry, 1, null));
+    }
+    return map;
+  }
+
   /** Support the STR_TO_MAP function. */
   public static Map strToMap(String string, String stringDelimiter, String 
keyValueDelimiter) {
     final Map map = new LinkedHashMap();
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 001c3fdea0..b7cbd3f65f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -752,6 +752,9 @@ public enum SqlKind {
   /** {@code SORT_ARRAY} function (Spark semantics). */
   SORT_ARRAY,
 
+  /** {@code MAP_CONCAT} function (Spark semantics). */
+  MAP_CONCAT,
+
   /** {@code MAP_ENTRIES} function (Spark semantics). */
   MAP_ENTRIES,
 
@@ -764,6 +767,9 @@ public enum SqlKind {
   /** {@code MAP_FROM_ARRAYS} function (Spark semantics). */
   MAP_FROM_ARRAYS,
 
+  /** {@code MAP_FROM_ENTRIES} function (Spark semantics). */
+  MAP_FROM_ENTRIES,
+
   /** {@code STR_TO_MAP} function (Spark semantics). */
   STR_TO_MAP,
 
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 6f7485f5e0..58fbaab72a 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
@@ -65,6 +65,7 @@ import static org.apache.calcite.sql.fun.SqlLibrary.MYSQL;
 import static org.apache.calcite.sql.fun.SqlLibrary.ORACLE;
 import static org.apache.calcite.sql.fun.SqlLibrary.POSTGRESQL;
 import static org.apache.calcite.sql.fun.SqlLibrary.SPARK;
+import static org.apache.calcite.util.Static.RESOURCE;
 
 import static java.util.Objects.requireNonNull;
 
@@ -1168,6 +1169,33 @@ public abstract class SqlLibraryOperators {
           ReturnTypes.ARG0_NULLABLE,
           OperandTypes.ARRAY.or(OperandTypes.ARRAY_BOOLEAN_LITERAL));
 
+  private static RelDataType deriveTypeMapConcat(SqlOperatorBinding opBinding) 
{
+    if (opBinding.getOperandCount() == 0) {
+      final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
+      final RelDataType type = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+      requireNonNull(type, "type");
+      return SqlTypeUtil.createMapType(typeFactory, type, type, true);
+    } else {
+      final List<RelDataType> operandTypes = opBinding.collectOperandTypes();
+      for (RelDataType operandType: operandTypes) {
+        if (!SqlTypeUtil.isMap(operandType)) {
+          throw opBinding.newError(
+              RESOURCE.typesShouldAllBeMap(
+                  opBinding.getOperator().getName(),
+                  operandType.getFullTypeString()));
+        }
+      }
+      return 
requireNonNull(opBinding.getTypeFactory().leastRestrictive(operandTypes));
+    }
+  }
+
+  /** The "MAP_CONCAT(map [, map]*)" function. */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction MAP_CONCAT =
+      SqlBasicFunction.create(SqlKind.MAP_CONCAT,
+          SqlLibraryOperators::deriveTypeMapConcat,
+          OperandTypes.SAME_VARIADIC);
+
   /** The "MAP_ENTRIES(map)" function. */
   @LibraryOperator(libraries = {SPARK})
   public static final SqlFunction MAP_ENTRIES =
@@ -1207,6 +1235,24 @@ public abstract class SqlLibraryOperators {
           SqlLibraryOperators::deriveTypeMapFromArrays,
           OperandTypes.ARRAY_ARRAY);
 
+  private static RelDataType deriveTypeMapFromEntries(SqlOperatorBinding 
opBinding) {
+    final RelDataType entriesType = opBinding.collectOperandTypes().get(0);
+    final RelDataType entryType = entriesType.getComponentType();
+    requireNonNull(entryType, () -> "componentType of " + entriesType);
+    return SqlTypeUtil.createMapType(
+        opBinding.getTypeFactory(),
+        requireNonNull(entryType.getFieldList().get(0).getType(), "inferred 
key type"),
+        requireNonNull(entryType.getFieldList().get(1).getType(), "inferred 
value type"),
+        entriesType.isNullable() || entryType.isNullable());
+  }
+
+  /** The "MAP_FROM_ENTRIES(arrayOfEntries)" function. */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction MAP_FROM_ENTRIES =
+      SqlBasicFunction.create(SqlKind.MAP_FROM_ENTRIES,
+          SqlLibraryOperators::deriveTypeMapFromEntries,
+          OperandTypes.MAP_FROM_ENTRIES);
+
   /** The "STR_TO_MAP(string[, stringDelimiter[, keyValueDelimiter]])" 
function. */
   @LibraryOperator(libraries = {SPARK})
   public static final SqlFunction STR_TO_MAP =
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 37f0f8325d..63b5308abd 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
@@ -527,6 +527,9 @@ public abstract class OperandTypes {
   public static final SqlOperandTypeChecker ARRAY_ELEMENT =
       new ArrayElementOperandTypeChecker();
 
+  public static final SqlSingleOperandTypeChecker MAP_FROM_ENTRIES =
+      new MapFromEntriesOperandTypeChecker();
+
   /**
    * Operand type-checking strategy where type must be a literal or NULL.
    */
@@ -1125,6 +1128,32 @@ public abstract class OperandTypes {
         }
       };
 
+  /** Checker that returns whether a value is a array of record type with two 
fields. */
+  private static class MapFromEntriesOperandTypeChecker
+      implements SqlSingleOperandTypeChecker {
+    @Override public boolean checkSingleOperandType(SqlCallBinding callBinding,
+        SqlNode node, int iFormalOperand, boolean throwOnFailure) {
+      assert 0 == iFormalOperand;
+      RelDataType type = SqlTypeUtil.deriveType(callBinding, node);
+      RelDataType componentType = requireNonNull(type.getComponentType(), 
"componentType");
+      boolean valid = false;
+      if (type.getSqlTypeName() == SqlTypeName.ARRAY
+          && componentType.getSqlTypeName() == SqlTypeName.ROW
+          && componentType.getFieldCount() == 2) {
+        valid = true;
+      }
+      if (!valid && throwOnFailure) {
+        throw callBinding.newValidationSignatureError();
+      }
+      return valid;
+    }
+
+    @Override public String getAllowedSignatures(SqlOperator op, String 
opName) {
+      return SqlUtil.getAliasedSignature(op, opName,
+          ImmutableList.of("ARRAY<RECORDTYPE(TWO FIELDS)>"));
+    }
+  }
+
   /** Operand type-checker that accepts period types. Examples:
    *
    * <ul>
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 ca987cc6e5..1b49de90b9 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -656,10 +656,12 @@ public enum BuiltInMethod {
   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_CONCAT(SqlFunctions.class, "mapConcat", Map[].class),
   MAP_ENTRIES(SqlFunctions.class, "mapEntries", Map.class),
   MAP_KEYS(SqlFunctions.class, "mapKeys", Map.class),
   MAP_VALUES(SqlFunctions.class, "mapValues", Map.class),
   MAP_FROM_ARRAYS(SqlFunctions.class, "mapFromArrays", List.class, List.class),
+  MAP_FROM_ENTRIES(SqlFunctions.class, "mapFromEntries", List.class),
   STR_TO_MAP(SqlFunctions.class, "strToMap", String.class, String.class, 
String.class),
   SELECTIVITY(Selectivity.class, "getSelectivity", RexNode.class),
   UNIQUE_KEYS(UniqueKeys.class, "getUniqueKeys", boolean.class),
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 4b18925e69..ca9003edfd 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -230,6 +230,7 @@ DuplicateNameInColumnList=Duplicate name ''{0}'' in column 
list
 RequireAtLeastOneArg=Require at least 1 argument
 MapRequiresTwoOrMoreArgs=Map requires at least 2 arguments
 MapRequiresEvenArgCount=Map requires an even number of arguments
+TypesShouldAllBeMap=Function ''{0}'' should all be of type map, but it is 
''{1}''
 IncompatibleTypes=Incompatible types
 ColumnCountMismatch=Number of columns must match number of query columns
 DuplicateColumnAndNoColumnList=Column has duplicate column name ''{0}'' and no 
column list specified
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 852559b7e0..3f5052db8b 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2753,11 +2753,13 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | m | TO_BASE64(string)                              | Converts the *string* 
to base-64 encoded form and returns a encoded string
 | b m | FROM_BASE64(string)                          | Returns the decoded 
result of a base-64 *string* as a string
 | b o | LTRIM(string)                                | Returns *string* with 
all blanks removed from the start
+| s | MAP_CONCAT(map [, map]*)                       | Concatenates one or 
more maps. If any input argument is `NULL` the function returns `NULL`. Note 
that calcite is using the LAST_WIN strategy
 | s | MAP_ENTRIES(map)                               | Returns the entries of 
the *map* as an array, the order of the entries is not defined
 | s | MAP_KEYS(map)                                  | Returns the keys of the 
*map* as an array, the order of the entries is not defined
 | s | MAP_VALUES(map)                                | Returns the values of 
the *map* as an array, the order of the entries is not defined
-| s | MAP_FROM_ARRAYS(array1, array2)                | Returns a map created 
from an *array1* and *array2*. Note that the lengths of two arrays should be 
the same
-| s | STR_TO_MAP(string [, stringDelimiter [, keyValueDelimiter]]) | Returns a 
map after splitting the *string* into key/value pairs using delimiters. Default 
delimiters are ',' for *stringDelimiter* and ':' for *keyValueDelimiter*
+| s | MAP_FROM_ARRAYS(array1, array2)                | Returns a map created 
from an *array1* and *array2*. Note that the lengths of two arrays should be 
the same and calcite is using the LAST_WIN strategy
+| s | MAP_FROM_ENTRIES(arrayOfRows)                  | Returns a map created 
from an arrays of row with two fields. Note that the number of fields in a row 
must be 2. Note that calcite is using the LAST_WIN strategy
+| s | STR_TO_MAP(string [, stringDelimiter [, keyValueDelimiter]]) | Returns a 
map after splitting the *string* into key/value pairs using delimiters. Default 
delimiters are ',' for *stringDelimiter* and ':' for *keyValueDelimiter*. Note 
that calcite is using the LAST_WIN strategy
 | b m p | MD5(string)                                | Calculates an MD5 
128-bit checksum of *string* and returns it as a hex string
 | m | MONTHNAME(date)                                | Returns the name, in 
the connection's locale, of the month in *datetime*; for example, it returns 
'二月' for both DATE '2020-02-10' and TIMESTAMP '2020-02-10 10:10:10'
 | o | NVL(value1, value2)                            | Returns *value1* if 
*value1* is not null, otherwise *value2*
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 e57c2da9dd..5a67eddf4d 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -5969,6 +5969,55 @@ public class SqlOperatorTest {
             + "'SORT_ARRAY\\(<ARRAY>, <BOOLEAN>\\)'", false);
   }
 
+  /** Tests {@code MAP_CONCAT} function from Spark. */
+  @Test void testMapConcatFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.MAP_CONCAT);
+    f0.checkFails("^map_concat(map['foo', 1], map['bar', 2])^",
+        "No match found for function signature MAP_CONCAT\\("
+            + "<\\(CHAR\\(3\\), INTEGER\\) MAP>, <\\(CHAR\\(3\\), INTEGER\\) 
MAP>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("map_concat(map['foo', 1], map['bar', 2])", "{foo=1, bar=2}",
+        "(CHAR(3) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_concat(map['foo', 1], map['bar', 2], map['foo', 2])", 
"{foo=2, bar=2}",
+        "(CHAR(3) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_concat(map[null, 1], map[null, 2])", "{null=2}",
+        "(NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_concat(map[1, 2], map[1, null])", "{1=null}",
+        "(INTEGER NOT NULL, INTEGER) MAP NOT NULL");
+    // test zero arg, but it should return empty map.
+    f.checkScalar("map_concat()", "{}",
+        "(VARCHAR NOT NULL, VARCHAR NOT NULL) MAP");
+
+    // after calcite supports cast(null as map<string, int>), it should add 
these tests.
+    if (TODO) {
+      f.checkNull("map_concat(map['foo', 1], cast(null as map<string, int>))");
+      f.checkType("map_concat(map['foo', 1], cast(null as map<string, int>))",
+          "(VARCHAR NOT NULL, INTEGER NOT NULL) MAP");
+      f.checkNull("map_concat(cast(null as map<string, int>), map['foo', 1])");
+      f.checkType("map_concat(cast(null as map<string, int>), map['foo', 1])",
+          "(VARCHAR NOT NULL, INTEGER NOT NULL) MAP");
+    }
+
+    // test only has one operand, but it is not map type.
+    f.checkFails("^map_concat(1)^",
+        "Function 'MAP_CONCAT' should all be of type map, but it is 'INTEGER 
NOT NULL'", false);
+    f.checkFails("^map_concat(null)^",
+        "Function 'MAP_CONCAT' should all be of type map, but it is 'NULL'", 
false);
+    // test operands in same type family, but it is not map type.
+    f.checkFails("^map_concat(array[1], array[1])^",
+        "Function 'MAP_CONCAT' should all be of type map, "
+            + "but it is 'INTEGER NOT NULL ARRAY NOT NULL'", false);
+    f.checkFails("^map_concat(map['foo', 1], null)^",
+        "Function 'MAP_CONCAT' should all be of type map, "
+            + "but it is 'NULL'", false);
+    // test operands not in same type family.
+    f.checkFails("^map_concat(map[1, null], array[1])^",
+        "Parameters must be of the same type", false);
+  }
+
+
   /** Tests {@code MAP_ENTRIES} function from Spark. */
   @Test void testMapEntriesFunc() {
     final SqlOperatorFixture f0 = fixture();
@@ -6050,6 +6099,48 @@ public class SqlOperatorTest {
         true);
   }
 
+  /** Tests {@code MAP_FROM_ENTRIES} function from Spark. */
+  @Test void testMapFromEntriesFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.MAP_FROM_ENTRIES);
+    f0.checkFails("^map_from_entries(array[row(1, 'a'), row(2, 'b')])^",
+        "No match found for function signature MAP_FROM_ENTRIES\\("
+            + "<RecordType\\(INTEGER EXPR\\$0, CHAR\\(1\\) EXPR\\$1\\) 
ARRAY>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("map_from_entries(array[row(1, 'a'), row(2, 'b')])", "{1=a, 
2=b}",
+        "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_entries(array[row(1, 'a'), row(1, 'b')])", "{1=b}",
+        "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_entries(array[row(null, 'a'), row(null, 'b')])", 
"{null=b}",
+        "(NULL, CHAR(1) NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_entries(array[row(1, 'a'), row(1, null)])", 
"{1=null}",
+        "(INTEGER NOT NULL, CHAR(1)) MAP NOT NULL");
+    f.checkScalar("map_from_entries(array[row(array['a'], 1), row(array['b'], 
2)])",
+        "{[a]=1, [b]=2}",
+        "(CHAR(1) NOT NULL ARRAY NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_entries(array[row(map['a', 1], 2), row(map['a', 
1], 3)])",
+        "{{a=1}=3}",
+        "((CHAR(1) NOT NULL, INTEGER NOT NULL) MAP NOT NULL, INTEGER NOT NULL) 
MAP NOT NULL");
+    f.checkType("map_from_entries(cast(null as row(f0 int, f1 varchar) 
array))",
+        "(INTEGER NOT NULL, VARCHAR NOT NULL) MAP");
+    f.checkNull("map_from_entries(cast(null as row(f0 int, f1 varchar) 
array))");
+    f.checkNull("map_from_entries(array[row(1, 'a'), null])");
+    f.checkType("map_from_entries(array[row(1, 'a'), null])",
+        "(INTEGER, CHAR(1)) MAP");
+
+    f.checkFails("^map_from_entries(array[1])^",
+        "Cannot apply 'MAP_FROM_ENTRIES' to arguments of type 
'MAP_FROM_ENTRIES\\("
+            + "<INTEGER ARRAY>\\)'. Supported form\\(s\\): 
'MAP_FROM_ENTRIES\\("
+            + "<ARRAY<RECORDTYPE\\(TWO FIELDS\\)>>\\)'",
+        false);
+    f.checkFails("^map_from_entries(array[row(1, 'a', 2)])^",
+        "Cannot apply 'MAP_FROM_ENTRIES' to arguments of type 
'MAP_FROM_ENTRIES\\("
+            + "<RECORDTYPE\\(INTEGER EXPR\\$0, CHAR\\(1\\) EXPR\\$1, INTEGER 
EXPR\\$2\\) ARRAY>\\)'. "
+            + "Supported form\\(s\\): 
'MAP_FROM_ENTRIES\\(<ARRAY<RECORDTYPE\\(TWO FIELDS\\)>>\\)'",
+        false);
+  }
+
   /** Tests {@code STR_TO_MAP} function from Spark. */
   @Test void testStrToMapFunc() {
     final SqlOperatorFixture f0 = fixture();

Reply via email to