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 95089c86fa [CALCITE-5744] Add MAP_FROM_ARRAYS, STR_TO_MAP function 
(enabled in Spark library)
95089c86fa is described below

commit 95089c86fafc504a34edda85915433adac419443
Author: yongen.ly <yongen...@alibaba-inc.com>
AuthorDate: Thu Jun 1 19:13:38 2023 +0800

    [CALCITE-5744] Add MAP_FROM_ARRAYS, STR_TO_MAP function (enabled in Spark 
library)
---
 .../calcite/adapter/enumerable/RexImpTable.java    | 37 +++++++++++
 .../apache/calcite/runtime/CalciteResource.java    |  3 +
 .../org/apache/calcite/runtime/SqlFunctions.java   | 28 ++++++++
 .../main/java/org/apache/calcite/sql/SqlKind.java  |  6 ++
 .../calcite/sql/fun/SqlLibraryOperators.java       | 25 +++++++
 .../org/apache/calcite/sql/type/OperandTypes.java  | 10 +++
 .../org/apache/calcite/sql/type/ReturnTypes.java   | 16 +++++
 .../apache/calcite/sql/type/SqlTypeTransforms.java | 11 ++++
 .../org/apache/calcite/util/BuiltInMethod.java     |  2 +
 .../calcite/runtime/CalciteResource.properties     |  1 +
 site/_docs/reference.md                            |  2 +
 .../org/apache/calcite/test/SqlOperatorTest.java   | 77 ++++++++++++++++++++++
 12 files changed, 218 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 f5a27adb3f..f1585e4e30 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
@@ -189,6 +189,7 @@ 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_ENTRIES;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.MAP_FROM_ARRAYS;
 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;
@@ -223,6 +224,7 @@ import static 
org.apache.calcite.sql.fun.SqlLibraryOperators.SPACE;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.SPLIT;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.STARTS_WITH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.STRCMP;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.STR_TO_MAP;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TANH;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIME;
 import static org.apache.calcite.sql.fun.SqlLibraryOperators.TIMESTAMP;
@@ -436,6 +438,8 @@ public class RexImpTable {
       Expressions.constant(true);
   public static final ConstantExpression COMMA_EXPR =
       Expressions.constant(",");
+  public static final ConstantExpression COLON_EXPR =
+      Expressions.constant(":");
   public static final MemberExpression BOXED_FALSE_EXPR =
       Expressions.field(null, Boolean.class, "FALSE");
   public static final MemberExpression BOXED_TRUE_EXPR =
@@ -736,6 +740,8 @@ public class RexImpTable {
       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);
+      map.put(STR_TO_MAP, new StringToMapImplementor());
       map.put(ARRAY_CONCAT, new ArrayConcatImplementor());
       map.put(SORT_ARRAY, new SortArrayImplementor());
       final MethodImplementor isEmptyImplementor =
@@ -3161,6 +3167,37 @@ public class RexImpTable {
     }
   }
 
+  /** Implementor for str_to_map. */
+  private static class StringToMapImplementor extends 
AbstractRexCallImplementor {
+    StringToMapImplementor() {
+      super("str_to_map", NullPolicy.STRICT, false);
+    }
+
+    @Override Expression implementSafe(RexToLixTranslator translator,
+        RexCall call, List<Expression> argValueList) {
+      switch (call.getOperands().size()) {
+      case 1:
+        return Expressions.call(
+            BuiltInMethod.STR_TO_MAP.method,
+            argValueList.get(0),
+            COMMA_EXPR,
+            COLON_EXPR);
+      case 2:
+        return Expressions.call(
+            BuiltInMethod.STR_TO_MAP.method,
+            argValueList.get(0),
+            argValueList.get(1),
+            COLON_EXPR);
+      case 3:
+        return Expressions.call(
+            BuiltInMethod.STR_TO_MAP.method,
+            argValueList);
+      default:
+        throw new AssertionError();
+      }
+    }
+  }
+
   /** Implementor for a array or string concat. */
   private static class ConcatImplementor extends AbstractRexCallImplementor {
     final ArrayConcatImplementor arrayConcatImplementor =
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 fc8fd6fb1e..27024331bc 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -900,6 +900,9 @@ public interface CalciteResource {
   @BaseMessage("Substring error: negative substring length not allowed")
   ExInst<CalciteException> illegalNegativeSubstringLength();
 
+  @BaseMessage("Illegal arguments: The length of the keys array {0,number,#} 
is not equal to the length of the values array {1,number,#} in MAP_FROM_ARRAYS 
function")
+  ExInst<CalciteException> illegalArgumentsInMapFromArraysFunc(int arg0, int 
arg1);
+
   @BaseMessage("Trim error: trim character must be exactly 1 character")
   ExInst<CalciteException> trimError();
 
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 26990d03f6..0638481ae2 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -92,6 +92,7 @@ import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -4058,6 +4059,33 @@ public class SqlFunctions {
     return new ArrayList<>(map.values());
   }
 
+  /** Support the MAP_FROM_ARRAYS function. */
+  public static Map mapFromArrays(List keysArray, List valuesArray) {
+    if (keysArray.size() != valuesArray.size()) {
+      throw RESOURCE.illegalArgumentsInMapFromArraysFunc(keysArray.size(), 
valuesArray.size()).ex();
+    }
+    final Map map = new LinkedHashMap<>();
+    for (int i = 0; i < keysArray.size(); i++) {
+      map.put(keysArray.get(i), valuesArray.get(i));
+    }
+    return map;
+  }
+
+  /** Support the STR_TO_MAP function. */
+  public static Map strToMap(String string, String stringDelimiter, String 
keyValueDelimiter) {
+    final Map map = new LinkedHashMap();
+    final String[] keyValues = string.split(stringDelimiter, -1);
+    for (String s : keyValues) {
+      String[] keyValueArray = s.split(keyValueDelimiter, 2);
+      String key = keyValueArray[0];
+      String value = keyValueArray.length < 2
+          ? null
+          : keyValueArray[1];
+      map.put(key, value);
+    }
+    return map;
+  }
+
   /** Support the SLICE function. */
   public static List slice(List list) {
     List result = new ArrayList(list.size());
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 a49423d684..ebeeb7f839 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,12 @@ public enum SqlKind {
   /** {@code MAP_VALUES} function (Spark semantics). */
   MAP_VALUES,
 
+  /** {@code MAP_FROM_ARRAYS} function (Spark semantics). */
+  MAP_FROM_ARRAYS,
+
+  /** {@code STR_TO_MAP} function (Spark semantics). */
+  STR_TO_MAP,
+
   /** {@code REVERSE} function (SQL Server, MySQL). */
   REVERSE,
 
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 4ceffd43e7..d42beb1ec5 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
@@ -1146,6 +1146,31 @@ public abstract class SqlLibraryOperators {
           ReturnTypes.TO_MAP_VALUES_NULLABLE,
           OperandTypes.MAP);
 
+  private static RelDataType deriveTypeMapFromArrays(SqlOperatorBinding 
opBinding) {
+    final RelDataType keysArrayType = opBinding.getOperandType(0);
+    final RelDataType valuesArrayType = opBinding.getOperandType(1);
+    final boolean nullable = keysArrayType.isNullable() || 
valuesArrayType.isNullable();
+    return SqlTypeUtil.createMapType(
+        opBinding.getTypeFactory(),
+        requireNonNull(keysArrayType.getComponentType(), "inferred key type"),
+        requireNonNull(valuesArrayType.getComponentType(), "inferred value 
type"),
+        nullable);
+  }
+
+  /** The "MAP_FROM_ARRAYS(keysArray, valuesArray)" function. */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction MAP_FROM_ARRAYS =
+      SqlBasicFunction.create(SqlKind.MAP_FROM_ARRAYS,
+          SqlLibraryOperators::deriveTypeMapFromArrays,
+          OperandTypes.ARRAY_ARRAY);
+
+  /** The "STR_TO_MAP(string[, stringDelimiter[, keyValueDelimiter]])" 
function. */
+  @LibraryOperator(libraries = {SPARK})
+  public static final SqlFunction STR_TO_MAP =
+      SqlBasicFunction.create(SqlKind.STR_TO_MAP,
+          ReturnTypes.IDENTITY_TO_MAP_NULLABLE,
+          OperandTypes.STRING_OPTIONAL_STRING_OPTIONAL_STRING);
+
   @LibraryOperator(libraries = {BIG_QUERY, MYSQL})
   public static final SqlFunction REVERSE =
       SqlBasicFunction.create(SqlKind.REVERSE,
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 6e85fa8c0c..37f0f8325d 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
@@ -404,6 +404,13 @@ public abstract class OperandTypes {
           // Third operand optional (operand index 0, 1, 2)
           number -> number == 2);
 
+  public static final FamilyOperandTypeChecker 
STRING_OPTIONAL_STRING_OPTIONAL_STRING =
+      family(
+          ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.STRING,
+              SqlTypeFamily.STRING),
+          // Second and Third operand both are optional (operand index 0, 1, 2)
+          number -> number == 1 || number == 2);
+
   public static final FamilyOperandTypeChecker STRING_NUMERIC_OPTIONAL_STRING =
       family(
           ImmutableList.of(SqlTypeFamily.STRING, SqlTypeFamily.NUMERIC,
@@ -469,6 +476,9 @@ public abstract class OperandTypes {
   public static final SqlSingleOperandTypeChecker ARRAY =
       family(SqlTypeFamily.ARRAY);
 
+  public static final SqlSingleOperandTypeChecker ARRAY_ARRAY =
+      family(SqlTypeFamily.ARRAY, SqlTypeFamily.ARRAY);
+
   public static final SqlSingleOperandTypeChecker ARRAY_OR_MAP =
       OperandTypes.family(SqlTypeFamily.ARRAY)
           .or(OperandTypes.family(SqlTypeFamily.MAP))
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 901d3dc395..5b9d7aa73c 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
@@ -628,6 +628,22 @@ public abstract class ReturnTypes {
   public static final SqlReturnTypeInference TO_MAP =
       ARG0.andThen(SqlTypeTransforms.TO_MAP);
 
+  /**
+   * Returns a MAP type.
+   *
+   * <p>For example, given {@code STRING}, returns
+   * {@code (STRING, STRING) MAP}.
+   */
+  public static final SqlReturnTypeInference IDENTITY_TO_MAP =
+      ARG0.andThen(SqlTypeTransforms.IDENTITY_TO_MAP);
+
+  /**
+   * Same as {@link #IDENTITY_TO_MAP} but returns with nullability if any of 
the
+   * operands is nullable.
+   */
+  public static final SqlReturnTypeInference IDENTITY_TO_MAP_NULLABLE =
+      IDENTITY_TO_MAP.andThen(SqlTypeTransforms.TO_NULLABLE);
+
   /**
    * Returns a ROW type.
    *
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 97562713c9..b9c6f80aa1 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
@@ -244,6 +244,17 @@ public abstract class SqlTypeTransforms {
           SqlTypeUtil.createMapTypeFromRecord(opBinding.getTypeFactory(),
               typeToTransform);
 
+  /**
+   * Parameter type-inference transform strategy that converts a type to a MAP 
type,
+   * which key and value type is same.
+   *
+   * @see org.apache.calcite.rel.type.RelDataTypeFactory#createMapType
+   */
+  public static final SqlTypeTransform IDENTITY_TO_MAP =
+      (opBinding, typeToTransform) ->
+          SqlTypeUtil.createMapType(opBinding.getTypeFactory(),
+              typeToTransform, typeToTransform, false);
+
   /**
    * Parameter type-inference transform strategy that converts a MAP type
    * to a two-field record type.
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 6dda17513f..64073b3977 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -655,6 +655,8 @@ public enum BuiltInMethod {
   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),
+  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),
   AVERAGE_ROW_SIZE(Size.class, "averageRowSize"),
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 c402732059..4b18925e69 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -294,6 +294,7 @@ DialectDoesNotSupportFeature=Dialect does not support 
feature: ''{0}''
 IllegalNegativePadLength=Second argument for LPAD/RPAD must not be negative
 IllegalEmptyPadPattern=Third argument (pad pattern) for LPAD/RPAD must not be 
empty
 IllegalNegativeSubstringLength=Substring error: negative substring length not 
allowed
+IllegalArgumentsInMapFromArraysFunc=Illegal arguments: The length of the keys 
array {0,number,#} is not equal to the length of the values array {1,number,#} 
in MAP_FROM_ARRAYS function
 TrimError=Trim error: trim character must be exactly 1 character
 InvalidTypesForArithmetic=Invalid types for arithmetic: {0} {1} {2}
 InvalidTypesForComparison=Invalid types for comparison: {0} {1} {2}
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index a701fe9eda..c45baae259 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2751,6 +2751,8 @@ BigQuery's type system uses confusingly different names 
for types and functions:
 | 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*
 | 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 2add67eb87..cd11a0f8fb 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -5866,6 +5866,82 @@ public class SqlOperatorTest {
         "INTEGER ARRAY NOT NULL");
   }
 
+  /** Tests {@code MAP_FROM_ARRAYS} function from Spark. */
+  @Test void testMapFromArraysFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.MAP_FROM_ARRAYS);
+    f0.checkFails("^map_from_arrays(array[1, 2], array['foo', 'bar'])^",
+        "No match found for function signature MAP_FROM_ARRAYS\\(<INTEGER 
ARRAY>, "
+            + "<CHAR\\(3\\) ARRAY>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("map_from_arrays(array[1, 2], array['foo', 'bar'])", 
"{1=foo, 2=bar}",
+        "(INTEGER NOT NULL, CHAR(3) NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_arrays(array[1, 1, null], array['foo', 'bar', 
'name'])",
+        "{1=bar, null=name}", "(INTEGER, CHAR(4) NOT NULL) MAP NOT NULL");
+    f.checkScalar("map_from_arrays(array(), array())",
+        "{}", "(UNKNOWN NOT NULL, UNKNOWN NOT NULL) MAP NOT NULL");
+    f.checkType("map_from_arrays(cast(null as integer array), array['foo', 
'bar'])",
+        "(INTEGER NOT NULL, CHAR(3) NOT NULL) MAP");
+    f.checkNull("map_from_arrays(cast(null as integer array), array['foo', 
'bar'])");
+
+    f.checkFails("^map_from_arrays(array[1, 2], 2)^",
+        "Cannot apply 'MAP_FROM_ARRAYS' to arguments of type 
'MAP_FROM_ARRAYS\\(<INTEGER ARRAY>,"
+            + " <INTEGER>\\)'. Supported form\\(s\\): 
'MAP_FROM_ARRAYS\\(<ARRAY>, <ARRAY>\\)'",
+        false);
+    f.checkFails("^map_from_arrays(2, array[1, 2])^",
+        "Cannot apply 'MAP_FROM_ARRAYS' to arguments of type 
'MAP_FROM_ARRAYS\\(<INTEGER>,"
+            + " <INTEGER ARRAY>\\)'. Supported form\\(s\\): 
'MAP_FROM_ARRAYS\\(<ARRAY>, <ARRAY>\\)'",
+        false);
+    f.checkFails("map_from_arrays(^array[1, '1', true]^, array['a', 'b', 
'c'])",
+        "Parameters must be of the same type",
+        false);
+    f.checkFails("map_from_arrays(array['a', 'b', 'c'], ^array[1, '1', 
true]^)",
+        "Parameters must be of the same type",
+        false);
+    f.checkFails("map_from_arrays(array[1, 2], array['foo'])",
+        "Illegal arguments: The length of the keys array 2 is not equal to the 
length "
+            + "of the values array 1 in MAP_FROM_ARRAYS function",
+        true);
+  }
+
+  /** Tests {@code STR_TO_MAP} function from Spark. */
+  @Test void testStrToMapFunc() {
+    final SqlOperatorFixture f0 = fixture();
+    f0.setFor(SqlLibraryOperators.STR_TO_MAP);
+    f0.checkFails("^str_to_map('a=1,b=2', ',', '=')^",
+        "No match found for function signature STR_TO_MAP\\("
+            + "<CHARACTER>, <CHARACTER>, <CHARACTER>\\)", false);
+
+    final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.SPARK);
+    f.checkScalar("str_to_map('a=1,b=2', ',', '=')", "{a=1, b=2}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a:1,b:2')", "{a=1, b=2}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a:1,b:2', ',')", "{a=1, b=2}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a=1&b=2', '&', '=')", "{a=1, b=2}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('k#2%v#3', '%', '#')", "{k=2, v=3}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a:1&b:2', '&')", "{a=1, b=2}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('k:2%v:3', '%')", "{k=2, v=3}",
+        "(CHAR(7) NOT NULL, CHAR(7) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a')", "{a=null}",
+        "(CHAR(1) NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a,b,c')", "{a=null, b=null, c=null}",
+        "(CHAR(5) NOT NULL, CHAR(5) NOT NULL) MAP NOT NULL");
+    f.checkScalar("str_to_map('a-b--c', '--')", "{a-b=null, c=null}",
+        "(CHAR(6) NOT NULL, CHAR(6) NOT NULL) MAP NOT NULL");
+    f.checkType("str_to_map(cast(null as varchar))",
+        "(VARCHAR, VARCHAR) MAP");
+    f.checkNull("str_to_map(cast(null as varchar))");
+    f.checkNull("str_to_map(null, ',', ':')");
+    f.checkNull("str_to_map('a:1,b:2,c:3', null, ':')");
+    f.checkNull("str_to_map('a:1,b:2,c:3', ',',null)");
+  }
+
   /** Tests {@code UNIX_SECONDS} and other datetime functions from BigQuery. */
   @Test void testUnixSecondsFunc() {
     SqlOperatorFixture f = fixture()
@@ -8510,6 +8586,7 @@ public class SqlOperatorTest {
     // empty array is illegal per SQL spec. presumably because one can't
     // infer type
     f.checkFails("^Array[]^", "Require at least 1 argument", false);
+    f.checkFails("^array[1, '1', true]^", "Parameters must be of the same 
type", false);
   }
 
   /** Test case for {@link SqlLibraryOperators#ARRAY} (Spark). */

Reply via email to