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

tanner 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 342cf606ac [CALCITE-5948] Use explicit casting if element type in 
ARRAY/MAP does not equal derived component type
342cf606ac is described below

commit 342cf606acc954228820f69f83d43298fd874184
Author: Ran Tao <[email protected]>
AuthorDate: Sun Aug 27 14:33:36 2023 +0800

    [CALCITE-5948] Use explicit casting if element type in ARRAY/MAP does not 
equal derived component type
---
 babel/src/test/resources/sql/big-query.iq          |   4 +-
 .../calcite/sql/fun/SqlArrayValueConstructor.java  |   5 +
 .../calcite/sql/fun/SqlLibraryOperators.java       |   5 +
 .../calcite/sql/fun/SqlMapValueConstructor.java    |   6 +
 .../calcite/sql/validate/SqlValidatorUtil.java     |  89 ++++++++++
 .../java/org/apache/calcite/test/JdbcTest.java     |   3 +-
 core/src/test/resources/sql/misc.iq                |  12 +-
 .../org/apache/calcite/test/SqlOperatorTest.java   | 193 +++++++++++++++++++--
 8 files changed, 293 insertions(+), 24 deletions(-)

diff --git a/babel/src/test/resources/sql/big-query.iq 
b/babel/src/test/resources/sql/big-query.iq
index 5455de9f57..9dce6abdd0 100755
--- a/babel/src/test/resources/sql/big-query.iq
+++ b/babel/src/test/resources/sql/big-query.iq
@@ -1012,9 +1012,9 @@ FROM
 
 SELECT
   email,
-  REGEXP_CONTAINS(email, '^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$')
+  REGEXP_CONTAINS(email, '^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)\s+$')
     AS valid_email_address,
-  REGEXP_CONTAINS(email, '^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$')
+  REGEXP_CONTAINS(email, '^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org\s+$')
     AS without_parentheses
 FROM
   (SELECT
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java
index e291c41211..5f6eb5dd82 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlArrayValueConstructor.java
@@ -20,6 +20,7 @@ import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperatorBinding;
 import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 
 import static java.util.Objects.requireNonNull;
 
@@ -38,6 +39,10 @@ public class SqlArrayValueConstructor extends 
SqlMultisetValueConstructor {
             opBinding.getTypeFactory(),
             opBinding.collectOperandTypes());
     requireNonNull(type, "inferred array element type");
+
+    // explicit cast elements to component type if they are not same
+    SqlValidatorUtil.adjustTypeForArrayConstructor(type, opBinding);
+
     return SqlTypeUtil.createArrayType(
         opBinding.getTypeFactory(), type, false);
   }
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 d58ae302d2..cdf4f76808 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
@@ -43,6 +43,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.type.SqlTypeTransforms;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Optionality;
 import org.apache.calcite.util.Static;
@@ -1066,6 +1067,10 @@ public abstract class SqlLibraryOperators {
               : opBinding.getTypeFactory().createUnknownType();
     }
     requireNonNull(type, "inferred array element type");
+
+    // explicit cast elements to component type if they are not same
+    SqlValidatorUtil.adjustTypeForArrayConstructor(type, opBinding);
+
     return SqlTypeUtil.createArrayType(opBinding.getTypeFactory(), type, 
false);
   }
 
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java
index d3c4e5840e..7f3da3bdb2 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMapValueConstructor.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlCallBinding;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperatorBinding;
 import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
@@ -44,10 +45,15 @@ public class SqlMapValueConstructor extends 
SqlMultisetValueConstructor {
     super("MAP", SqlKind.MAP_VALUE_CONSTRUCTOR);
   }
 
+  @SuppressWarnings("argument.type.incompatible")
   @Override public RelDataType inferReturnType(SqlOperatorBinding opBinding) {
     Pair<@Nullable RelDataType, @Nullable RelDataType> type =
         getComponentTypes(
             opBinding.getTypeFactory(), opBinding.collectOperandTypes());
+
+    // explicit cast elements to component type if they are not same
+    SqlValidatorUtil.adjustTypeForMapConstructor(type, opBinding);
+
     return SqlTypeUtil.createMapType(
         opBinding.getTypeFactory(),
         requireNonNull(type.left, "inferred key type"),
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
index 0825ec2b21..6d1119c9a4 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java
@@ -38,6 +38,7 @@ import org.apache.calcite.schema.Table;
 import org.apache.calcite.schema.impl.AbstractSchema;
 import org.apache.calcite.schema.impl.AbstractTable;
 import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlCallBinding;
 import org.apache.calcite.sql.SqlDataTypeSpec;
 import org.apache.calcite.sql.SqlDynamicParam;
 import org.apache.calcite.sql.SqlFunctionCategory;
@@ -48,6 +49,7 @@ import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlOperatorBinding;
 import org.apache.calcite.sql.SqlOperatorTable;
 import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlSyntax;
@@ -1309,6 +1311,93 @@ public class SqlValidatorUtil {
     return null;
   }
 
+  /**
+   * When the array element does not equal the array component type, make 
explicit casting.
+   *
+   * @param componentType derived array component type
+   * @param opBinding description of call
+   */
+  public static void adjustTypeForArrayConstructor(
+      RelDataType componentType, SqlOperatorBinding opBinding) {
+    if (opBinding instanceof SqlCallBinding) {
+      requireNonNull(componentType, "array component type");
+      adjustTypeForMultisetConstructor(
+          componentType, componentType, (SqlCallBinding) opBinding);
+    }
+  }
+
+  /**
+   * When the map key or value does not equal the map component key type or 
value type,
+   * make explicit casting.
+   *
+   * @param componentType derived map pair component type
+   * @param opBinding description of call
+   */
+  public static void adjustTypeForMapConstructor(
+      Pair<RelDataType, RelDataType> componentType, SqlOperatorBinding 
opBinding) {
+    if (opBinding instanceof SqlCallBinding) {
+      requireNonNull(componentType.getKey(), "map key type");
+      requireNonNull(componentType.getValue(), "map value type");
+      adjustTypeForMultisetConstructor(
+          componentType.getKey(), componentType.getValue(), (SqlCallBinding) 
opBinding);
+    }
+  }
+
+  /**
+   * Adjusts the types for operands in a SqlCallBinding during the 
construction of a sql collection
+   * type such as Array or Map. This method iterates from the operands of a 
{@link SqlCall}
+   * obtained from the provided {@link SqlCallBinding}.
+   * It modifies each operand to match the specified 'evenType' or 'oddType' 
depending on whether
+   * the operand's position is even or odd, respectively. The type adjustment 
is performed by
+   * explicitly casting each operand to the desired type if the existing 
operand type does not
+   * match the desired type without considering field names.
+   * If we adjust array, we should set 'evenType' and 'oddType' to same 
desired type,
+   * if we adjust map, we should set 'evenType' as map key type and 'oddType' 
as map value type.
+   *
+   * <p>For example, if the operand types are {@code [INT, STRING, INT, 
STRING]}
+   * with {@code evenType} as {@code BOOLEAN} and {@code oddType} as {@code 
DOUBLE},
+   * after executing this method, the types should be {@code [BOOLEAN, DOUBLE, 
BOOLEAN, DOUBLE]},
+   * then the corresponding operands are cast to these types.
+   *
+   * @param evenType the {@link RelDataType} to which the operands at even 
positions should be cast
+   * @param oddType the {@link RelDataType} to which the operands at odd 
positions should be cast
+   * @param sqlCallBinding the {@link SqlCallBinding} containing the operands 
to be adjusted
+   */
+  private static void adjustTypeForMultisetConstructor(
+      RelDataType evenType, RelDataType oddType, SqlCallBinding 
sqlCallBinding) {
+    SqlCall call = sqlCallBinding.getCall();
+    List<RelDataType> operandTypes = sqlCallBinding.collectOperandTypes();
+    List<SqlNode> operands = call.getOperandList();
+    RelDataType elementType;
+    for (int i = 0; i < operands.size(); i++) {
+      if (i % 2 == 0) {
+        elementType = evenType;
+      } else {
+        elementType = oddType;
+      }
+      if (!operandTypes.get(i).equalsSansFieldNames(elementType)) {
+        call.setOperand(i, castTo(operands.get(i), elementType));
+      }
+    }
+  }
+
+  /**
+   * Creates a CAST operation to cast a given {@link SqlNode} to a specified 
{@link RelDataType}.
+   * This method uses the {@link SqlStdOperatorTable#CAST} operator to create 
a new {@link SqlCall}
+   * node representing a CAST operation. The original 'node' is cast to the 
desired 'type',
+   * preserving the nullability of the 'type'.
+   *
+   * @param node the {@link SqlNode} which is to be cast
+   * @param type the target {@link RelDataType} to which 'node' should be cast
+   * @return a new {@link SqlNode} representing the CAST operation
+   */
+  private static SqlNode castTo(SqlNode node, RelDataType type) {
+    return SqlStdOperatorTable.CAST.createCall(
+        SqlParserPos.ZERO,
+        node,
+        SqlTypeUtil.convertTypeToSpec(type).withNullable(type.isNullable()));
+  }
+
   //~ Inner Classes ----------------------------------------------------------
 
   /**
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 8abb94f7dc..c02bff87bc 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -8144,10 +8144,9 @@ public class JdbcTest {
    * ClassCastException retrieving from ARRAY that has mixed INTEGER and 
DECIMAL
    * elements</a>. */
   @Test void testIntAndBigDecimalInArray() {
-    // Result should be "EXPR$0=[1, 1.1]\n"; [CALCITE-4850] logged.
     CalciteAssert.that()
         .query("select array[1, 1.1]")
-        .returns("EXPR$0=[0E+1, 1.1]\n");
+        .returns("EXPR$0=[1, 1.1]\n");
   }
 
   /** Test case for
diff --git a/core/src/test/resources/sql/misc.iq 
b/core/src/test/resources/sql/misc.iq
index 7e10550455..8bdb345b22 100644
--- a/core/src/test/resources/sql/misc.iq
+++ b/core/src/test/resources/sql/misc.iq
@@ -2207,12 +2207,12 @@ select array[1,null,2] as a from (values (1));
 
 values array['a',null,'bcd'],
   array['efgh'];
-+----------------+
-| EXPR$0         |
-+----------------+
-| [a, null, bcd] |
-| [efgh]         |
-+----------------+
++------------------+
+| EXPR$0           |
++------------------+
+| [a  , null, bcd] |
+| [efgh]           |
++------------------+
 (2 rows)
 
 !ok
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 60bd95846d..03939830cc 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -6001,6 +6001,13 @@ public class SqlOperatorTest {
     f.checkScalar("array_compact(array())", "[]",
         "UNKNOWN NOT NULL ARRAY NOT NULL");
     f.checkNull("array_compact(null)");
+    // elements cast
+    f.checkScalar("array_compact(array[null, 1, null, cast(2 as tinyint)])", 
"[1, 2]",
+        "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_compact(array[null, 1, null, cast(2 as bigint)])", 
"[1, 2]",
+        "BIGINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_compact(array[null, 1, null, cast(2 as decimal)])", 
"[1, 2]",
+        "DECIMAL(19, 0) NOT NULL ARRAY NOT NULL");
   }
 
   /** Tests {@code ARRAY_CONCAT} function from BigQuery. */
@@ -6063,6 +6070,13 @@ public class SqlOperatorTest {
     f.checkScalar("array_distinct(array[null, 1, null])", "[null, 1]",
         "INTEGER ARRAY NOT NULL");
     f.checkNull("array_distinct(null)");
+    // elements cast
+    f.checkScalar("array_distinct(array[null, cast(1 as tinyint), 1, cast(2 as 
smallint)])",
+        "[null, 1, 2]", "INTEGER ARRAY NOT NULL");
+    f.checkScalar("array_distinct(array[null, cast(1 as tinyint), 1, cast(2 as 
bigint)])",
+        "[null, 1, 2]", "BIGINT ARRAY NOT NULL");
+    f.checkScalar("array_distinct(array[null, cast(1 as tinyint), 1, cast(2 as 
decimal)])",
+        "[null, 1, 2]", "DECIMAL(19, 0) ARRAY NOT NULL");
   }
 
   @Test void testArrayJoinFunc() {
@@ -6072,16 +6086,30 @@ public class SqlOperatorTest {
         + " 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",
+    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",
+        "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['', 'b'], '-')", " -b", "VARCHAR NOT 
NULL");
     f.checkScalar("array_join(array['', ''], '-')", "-", "VARCHAR NOT NULL");
+
+    final SqlOperatorFixture f1 =
+        f.withConformance(SqlConformanceEnum.PRAGMATIC_2003);
+    f1.checkScalar("array_join(array['aa', 'b', 'c'], '-')", "aa-b-c",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_join(array[null, 'aa', null, 'b', null], '-', 
'empty')",
+        "empty-aa-empty-b-empty", "VARCHAR NOT NULL");
+    f1.checkScalar("array_join(array[null, 'aa', null, 'b', null], '-')", 
"aa-b",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_join(array[null, x'aa', null, x'bb', null], '-')", 
"aa-bb",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_join(array['', 'b'], '-')", "-b", "VARCHAR NOT 
NULL");
+    f1.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], '-', ' ')^",
@@ -6104,6 +6132,13 @@ public class SqlOperatorTest {
     f.checkType("array_max(array())", "UNKNOWN");
     f.checkNull("array_max(array())");
     f.checkNull("array_max(cast(null as integer array))");
+    // elements cast
+    f.checkScalar("array_max(array[null, 1, cast(2 as tinyint)])", "2",
+        "INTEGER");
+    f.checkScalar("array_max(array[null, 1, cast(2 as bigint)])", "2",
+        "BIGINT");
+    f.checkScalar("array_max(array[null, 1, cast(2 as decimal)])", "2",
+        "DECIMAL(19, 0)");
   }
 
   /** Tests {@code ARRAY_MIN} function from Spark. */
@@ -6119,6 +6154,13 @@ public class SqlOperatorTest {
     f.checkType("array_min(array())", "UNKNOWN");
     f.checkNull("array_min(array())");
     f.checkNull("array_min(cast(null as integer array))");
+    // elements cast
+    f.checkScalar("array_min(array[null, 1, cast(2 as tinyint)])", "1",
+        "INTEGER");
+    f.checkScalar("array_min(array[null, 1, cast(2 as bigint)])", "1",
+        "BIGINT");
+    f.checkScalar("array_min(array[null, 1, cast(2 as decimal)])", "1",
+        "DECIMAL(19, 0)");
   }
 
   /** Tests {@code ARRAY_POSITION} function from Spark. */
@@ -6236,6 +6278,13 @@ public class SqlOperatorTest {
         "(INTEGER NOT NULL, CHAR(1) NOT NULL) MAP NOT NULL ARRAY NOT NULL");
     f.checkScalar("array_repeat(cast(null as integer), 2)", "[null, null]",
         "INTEGER ARRAY NOT NULL");
+    // elements cast
+    f.checkScalar("array_repeat(cast(1 as tinyint), 2)", "[1, 1]",
+        "TINYINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_repeat(cast(1 as bigint), 2)", "[1, 1]",
+        "BIGINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_repeat(cast(1 as decimal), 2)", "[1, 1]",
+        "DECIMAL(19, 0) NOT NULL ARRAY NOT NULL");
     f.checkNull("array_repeat(1, null)");
   }
 
@@ -6252,6 +6301,19 @@ public class SqlOperatorTest {
         "INTEGER NOT NULL ARRAY NOT NULL");
     f.checkScalar("array_reverse(array[null, 1])", "[1, null]",
         "INTEGER ARRAY NOT NULL");
+    // elements cast
+    f.checkScalar("array_reverse(array[cast(1 as tinyint), 2])", "[2, 1]",
+        "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_reverse(array[null, 1, cast(2 as tinyint)])", "[2, 1, 
null]",
+        "INTEGER ARRAY NOT NULL");
+    f.checkScalar("array_reverse(array[cast(1 as bigint), 2])", "[2, 1]",
+        "BIGINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_reverse(array[null, 1, cast(2 as bigint)])", "[2, 1, 
null]",
+        "BIGINT ARRAY NOT NULL");
+    f.checkScalar("array_reverse(array[cast(1 as decimal), 2])", "[2, 1]",
+        "DECIMAL(19, 0) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("array_reverse(array[null, 1, cast(2 as decimal)])", "[2, 1, 
null]",
+        "DECIMAL(19, 0) ARRAY NOT NULL");
   }
 
   /** Tests {@code ARRAY_SIZE} function from Spark. */
@@ -6267,6 +6329,13 @@ public class SqlOperatorTest {
     f.checkScalar("array_size(array[1, 2, null])", "3",
         "INTEGER NOT NULL");
     f.checkNull("array_size(null)");
+    // elements cast
+    f.checkScalar("array_size(array[cast(1 as tinyint), 2])", "2",
+        "INTEGER NOT NULL");
+    f.checkScalar("array_size(array[null, 1, cast(2 as tinyint)])", "3",
+        "INTEGER NOT NULL");
+    f.checkScalar("array_size(array[cast(1 as bigint), 2])", "2",
+        "INTEGER NOT NULL");
   }
 
   /** Tests {@code ARRAY_LENGTH} function from BigQuery. */
@@ -6281,6 +6350,13 @@ public class SqlOperatorTest {
     f.checkScalar("array_length(array[1, 2, null])", "3",
         "INTEGER NOT NULL");
     f.checkNull("array_length(null)");
+    // elements cast
+    f.checkScalar("array_length(array[cast(1 as tinyint), 2])", "2",
+        "INTEGER NOT NULL");
+    f.checkScalar("array_length(array[null, 1, cast(2 as tinyint)])", "3",
+        "INTEGER NOT NULL");
+    f.checkScalar("array_length(array[cast(1 as bigint), 2])", "2",
+        "INTEGER NOT NULL");
   }
 
   @Test void testArrayToStringFunc() {
@@ -6290,15 +6366,15 @@ public class SqlOperatorTest {
         + " signature ARRAY_TO_STRING\\(<CHAR\\(2\\) ARRAY>, <CHARACTER>\\)", 
false);
 
     final SqlOperatorFixture f = f0.withLibrary(SqlLibrary.BIG_QUERY);
-    f.checkScalar("array_to_string(array['aa', 'b', 'c'], '-')", "aa-b-c",
+    f.checkScalar("array_to_string(array['aa', 'b', 'c'], '-')", "aa-b -c ",
         "VARCHAR NOT NULL");
     f.checkScalar("array_to_string(array[null, 'aa', null, 'b', null], '-', 
'empty')",
-        "empty-aa-empty-b-empty", "VARCHAR NOT NULL");
-    f.checkScalar("array_to_string(array[null, 'aa', null, 'b', null], '-')", 
"aa-b",
+        "empty-aa-empty-b -empty", "VARCHAR NOT NULL");
+    f.checkScalar("array_to_string(array[null, 'aa', null, 'b', null], '-')", 
"aa-b ",
         "VARCHAR NOT NULL");
     f.checkScalar("array_to_string(array[null, x'aa', null, x'bb', null], 
'-')", "aa-bb",
         "VARCHAR NOT NULL");
-    f.checkScalar("array_to_string(array['', 'b'], '-')", "-b", "VARCHAR NOT 
NULL");
+    f.checkScalar("array_to_string(array['', 'b'], '-')", " -b", "VARCHAR NOT 
NULL");
     f.checkScalar("array_to_string(array['', ''], '-')", "-", "VARCHAR NOT 
NULL");
     f.checkNull("array_to_string(null, '-')");
     f.checkNull("array_to_string(array['a', 'b', null], null)");
@@ -6306,6 +6382,19 @@ public class SqlOperatorTest {
         "Cannot apply 'ARRAY_TO_STRING' to arguments of type 'ARRAY_TO_STRING"
             + "\\(<INTEGER ARRAY>, <CHAR\\(1\\)>, <CHAR\\(1\\)>\\)'\\. 
Supported form\\(s\\):"
             + " ARRAY_TO_STRING\\(<STRING ARRAY>, <CHARACTER>\\[, 
<CHARACTER>\\]\\)", false);
+
+    final SqlOperatorFixture f1 =
+        f.withConformance(SqlConformanceEnum.PRAGMATIC_2003);
+    f1.checkScalar("array_to_string(array['aa', 'b', 'c'], '-')", "aa-b-c",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_to_string(array[null, 'aa', null, 'b', null], '-', 
'empty')",
+        "empty-aa-empty-b-empty", "VARCHAR NOT NULL");
+    f1.checkScalar("array_to_string(array[null, 'aa', null, 'b', null], '-')", 
"aa-b",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_to_string(array[null, x'aa', null, x'bb', null], 
'-')", "aa-bb",
+        "VARCHAR NOT NULL");
+    f1.checkScalar("array_to_string(array['', 'b'], '-')", "-b", "VARCHAR NOT 
NULL");
+    f1.checkScalar("array_to_string(array['', ''], '-')", "-", "VARCHAR NOT 
NULL");
   }
 
   /** Tests {@code ARRAY_EXCEPT} function from Spark. */
@@ -6549,6 +6638,16 @@ public class SqlOperatorTest {
         "UNKNOWN NOT NULL ARRAY NOT NULL");
     f.checkNull("sort_array(null)");
 
+    // elements cast
+    f.checkScalar("sort_array(array[cast(1 as tinyint), 2])", "[1, 2]",
+        "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("sort_array(array[null, 1, cast(2 as tinyint)])", "[null, 1, 
2]",
+        "INTEGER ARRAY NOT NULL");
+    f.checkScalar("sort_array(array[cast(1 as bigint), 2])", "[1, 2]",
+        "BIGINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("sort_array(array[cast(1 as decimal), 2])", "[1, 2]",
+        "DECIMAL(19, 0) NOT NULL ARRAY NOT NULL");
+
     f.checkFails("^sort_array(array[2, null, 1], cast(1 as boolean))^",
         "Argument to function 'SORT_ARRAY' must be a literal", false);
     f.checkFails("^sort_array(array[2, null, 1], 1)^",
@@ -6619,6 +6718,21 @@ public class SqlOperatorTest {
         "RecordType(CHAR(3) NOT NULL f0, INTEGER NOT NULL f1) NOT NULL ARRAY 
NOT NULL");
     f.checkScalar("map_entries(map['foo', 1, null, 2])", "[{foo, 1}, {null, 
2}]",
         "RecordType(CHAR(3) f0, INTEGER NOT NULL f1) NOT NULL ARRAY NOT NULL");
+    // elements cast
+    // key cast
+    f.checkScalar("map_entries(map[cast(1 as tinyint), 1, 2, 2])", "[{1, 1}, 
{2, 2}]",
+        "RecordType(INTEGER NOT NULL f0, INTEGER NOT NULL f1) NOT NULL ARRAY 
NOT NULL");
+    f.checkScalar("map_entries(map[cast(1 as bigint), 1, null, 2])", "[{1, 1}, 
{null, 2}]",
+        "RecordType(BIGINT f0, INTEGER NOT NULL f1) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("map_entries(map[cast(1 as decimal), 1, null, 2])", "[{1, 
1}, {null, 2}]",
+        "RecordType(DECIMAL(19, 0) f0, INTEGER NOT NULL f1) NOT NULL ARRAY NOT 
NULL");
+    // value cast
+    f.checkScalar("map_entries(map[1, cast(1 as tinyint), 2, 2])", "[{1, 1}, 
{2, 2}]",
+        "RecordType(INTEGER NOT NULL f0, INTEGER NOT NULL f1) NOT NULL ARRAY 
NOT NULL");
+    f.checkScalar("map_entries(map[1, cast(1 as bigint), null, 2])", "[{1, 1}, 
{null, 2}]",
+        "RecordType(INTEGER f0, BIGINT NOT NULL f1) NOT NULL ARRAY NOT NULL");
+    f.checkScalar("map_entries(map[1, cast(1 as decimal), null, 2])", "[{1, 
1}, {null, 2}]",
+        "RecordType(INTEGER f0, DECIMAL(19, 0) NOT NULL f1) NOT NULL ARRAY NOT 
NULL");
   }
 
   /** Tests {@code MAP_KEYS} function from Spark. */
@@ -6633,6 +6747,21 @@ public class SqlOperatorTest {
         "CHAR(3) NOT NULL ARRAY NOT NULL");
     f.checkScalar("map_keys(map['foo', 1, null, 2])", "[foo, null]",
         "CHAR(3) ARRAY NOT NULL");
+    // elements cast
+    // key cast
+    f.checkScalar("map_keys(map[cast(1 as tinyint), 1, 2, 2])", "[1, 2]",
+        "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("map_keys(map[cast(1 as bigint), 1, null, 2])", "[1, null]",
+        "BIGINT ARRAY NOT NULL");
+    f.checkScalar("map_keys(map[cast(1 as decimal), 1, null, 2])", "[1, null]",
+        "DECIMAL(19, 0) ARRAY NOT NULL");
+    // value cast
+    f.checkScalar("map_keys(map[1, cast(1 as tinyint), 2, 2])", "[1, 2]",
+        "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("map_keys(map[1, cast(1 as bigint), null, 2])", "[1, null]",
+        "INTEGER ARRAY NOT NULL");
+    f.checkScalar("map_keys(map[1, cast(1 as decimal), null, 2])", "[1, null]",
+        "INTEGER ARRAY NOT NULL");
   }
 
   /** Tests {@code MAP_VALUES} function from Spark. */
@@ -6661,7 +6790,13 @@ public class SqlOperatorTest {
     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");
+        "{1=bar , null=name}", "(INTEGER, CHAR(4) NOT NULL) MAP NOT NULL");
+
+    final SqlOperatorFixture f1 =
+        f.withConformance(SqlConformanceEnum.PRAGMATIC_2003);
+    f1.checkScalar("map_from_arrays(array[1, 1, null], array['foo', 'bar', 
'name'])",
+        "{1=bar, null=name}", "(INTEGER, VARCHAR(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'])",
@@ -9968,6 +10103,13 @@ public class SqlOperatorTest {
         "[foo, null]", "CHAR(3) ARRAY NOT NULL");
     f.checkScalar("Array[null]",
         "[null]", "NULL ARRAY NOT NULL");
+    // element cast
+    f.checkScalar("Array[cast(1 as tinyint), cast(2 as smallint)]",
+        "[1, 2]", "SMALLINT NOT NULL ARRAY NOT NULL");
+    f.checkScalar("Array[1, cast(2 as tinyint), cast(3 as smallint)]",
+        "[1, 2, 3]", "INTEGER NOT NULL ARRAY NOT NULL");
+    f.checkScalar("Array[1, cast(2 as tinyint), cast(3 as smallint), cast(4 as 
bigint)]",
+        "[1, 2, 3, 4]", "BIGINT NOT NULL ARRAY NOT NULL");
     // empty array is illegal per SQL spec. presumably because one can't
     // infer type
     f.checkFails("^Array[]^", "Require at least 1 argument", false);
@@ -9995,14 +10137,15 @@ public class SqlOperatorTest {
         "[null, foo]", "CHAR(3) ARRAY NOT NULL");
     f2.checkScalar("array(null)",
         "[null]", "NULL ARRAY NOT NULL");
+    // calcite default cast char type will fill extra spaces
     f2.checkScalar("array(1, 2, 'Hi')",
-        "[1, 2, Hi]", "CHAR(2) NOT NULL ARRAY NOT NULL");
+        "[1 , 2 , Hi]", "CHAR(2) NOT NULL ARRAY NOT NULL");
     f2.checkScalar("array(1, 2, 'Hi', 'Hello')",
-        "[1, 2, Hi, Hello]", "CHAR(5) NOT NULL ARRAY NOT NULL");
+        "[1    , 2    , Hi   , Hello]", "CHAR(5) NOT NULL ARRAY NOT NULL");
     f2.checkScalar("array(1, 2, 'Hi', null)",
-        "[1, 2, Hi, null]", "CHAR(2) ARRAY NOT NULL");
+        "[1 , 2 , Hi, null]", "CHAR(2) ARRAY NOT NULL");
     f2.checkScalar("array(1, 2, 'Hi', cast(null as char(10)))",
-        "[1, 2, Hi, null]", "CHAR(10) ARRAY NOT NULL");
+        "[1         , 2         , Hi        , null]", "CHAR(10) ARRAY NOT 
NULL");
   }
 
   /**
@@ -10169,14 +10312,36 @@ public class SqlOperatorTest {
     f.checkFails("^map[1, 1, 2, 'x']^",
         "Parameters must be of the same type", false);
     f.checkScalar("map['washington', 1, 'obama', 44]",
-        "{washington=1, obama=44}",
+        "{washington=1, obama     =44}",
         "(CHAR(10) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    // elements cast
+    f.checkScalar("map['A', 1, 'ABC', 2]", "{A  =1, ABC=2}",
+        "(CHAR(3) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("Map[cast(1 as tinyint), 1, cast(2 as smallint), 2]",
+        "{1=1, 2=2}", "(SMALLINT NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f.checkScalar("Map[1, cast(1 as tinyint), 2, cast(2 as smallint)]",
+        "{1=1, 2=2}", "(INTEGER NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
+    f.checkScalar("Map[1, cast(1 as tinyint), cast(2 as bigint), cast(2 as 
smallint)]",
+        "{1=1, 2=2}", "(BIGINT NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
+    f.checkScalar("Map[cast(1 as bigint), cast(1 as tinyint), 2, cast(2 as 
smallint)]",
+        "{1=1, 2=2}", "(BIGINT NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
 
     final SqlOperatorFixture f1 =
         f.withConformance(SqlConformanceEnum.PRAGMATIC_2003);
     f1.checkScalar("map['washington', 1, 'obama', 44]",
         "{washington=1, obama=44}",
         "(VARCHAR(10) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    // elements cast
+    f1.checkScalar("map['A', 1, 'ABC', 2]", "{A=1, ABC=2}",
+        "(VARCHAR(3) NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f1.checkScalar("Map[cast(1 as tinyint), 1, cast(2 as smallint), 2]",
+        "{1=1, 2=2}", "(SMALLINT NOT NULL, INTEGER NOT NULL) MAP NOT NULL");
+    f1.checkScalar("Map[1, cast(1 as tinyint), 2, cast(2 as smallint)]",
+        "{1=1, 2=2}", "(INTEGER NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
+    f1.checkScalar("Map[1, cast(1 as tinyint), cast(2 as bigint), cast(2 as 
smallint)]",
+        "{1=1, 2=2}", "(BIGINT NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
+    f1.checkScalar("Map[cast(1 as bigint), cast(1 as tinyint), 2, cast(2 as 
smallint)]",
+        "{1=1, 2=2}", "(BIGINT NOT NULL, SMALLINT NOT NULL) MAP NOT NULL");
   }
 
   @Test void testCeilFunc() {

Reply via email to