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

mbudiu 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 2fafa5254e [CALCITE-7114] Invalid unparse for cast to array type in 
Spark
2fafa5254e is described below

commit 2fafa5254e40ddcffbf1fcd9ae56c50954763dac
Author: xuzifu666 <[email protected]>
AuthorDate: Wed Jul 30 14:28:44 2025 +0800

    [CALCITE-7114] Invalid unparse for cast to array type in Spark
---
 .../calcite/sql/dialect/SparkSqlDialect.java       |  13 ++-
 .../calcite/sql/dialect/StarRocksSqlDialect.java   | 120 +++++++++------------
 .../apache/calcite/util/RelToSqlConverterUtil.java |  17 +++
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |  22 ++++
 4 files changed, 98 insertions(+), 74 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java
index eaf5acdc1e..42cd259e51 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/SparkSqlDialect.java
@@ -37,8 +37,6 @@
 import org.apache.calcite.sql.fun.SqlLibraryOperators;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.BasicSqlType;
-import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.RelToSqlConverterUtil;
 
 import com.google.common.collect.ImmutableList;
@@ -163,10 +161,19 @@ public SparkSqlDialect(SqlDialect.Context context) {
   }
 
   @Override public @Nullable SqlNode getCastSpec(RelDataType type) {
-    if (type instanceof BasicSqlType && type.getSqlTypeName() == 
SqlTypeName.VARCHAR) {
+    switch (type.getSqlTypeName()) {
+    case VARCHAR:
       return new SqlDataTypeSpec(
           new SqlAlienSystemTypeNameSpec("STRING", type.getSqlTypeName(),
               SqlParserPos.ZERO), SqlParserPos.ZERO);
+    case ARRAY:
+      return RelToSqlConverterUtil.getCastSpecAngleBracketArrayType(this, type,
+          SqlParserPos.ZERO);
+    case MULTISET:
+      throw new UnsupportedOperationException("Spark dialect does not support 
cast to "
+          + type.getSqlTypeName());
+    default:
+      break;
     }
     return super.getCastSpec(type);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java 
b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
index d7031666fe..07d09b5a24 100644
--- a/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
+++ b/core/src/main/java/org/apache/calcite/sql/dialect/StarRocksSqlDialect.java
@@ -25,7 +25,6 @@
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlAbstractDateTimeLiteral;
 import org.apache.calcite.sql.SqlAlienSystemTypeNameSpec;
-import org.apache.calcite.sql.SqlArrayWithAngleBracketsNameSpec;
 import org.apache.calcite.sql.SqlBasicTypeNameSpec;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlDataTypeSpec;
@@ -33,13 +32,9 @@
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlMapTypeNameSpec;
 import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlTypeNameSpec;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.fun.SqlFloorFunction;
 import org.apache.calcite.sql.parser.SqlParserPos;
-import org.apache.calcite.sql.type.AbstractSqlType;
-import org.apache.calcite.sql.type.ArraySqlType;
-import org.apache.calcite.sql.type.BasicSqlType;
 import org.apache.calcite.sql.type.MapSqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.RelToSqlConverterUtil;
@@ -177,72 +172,55 @@ public StarRocksSqlDialect(Context context) {
   }
 
   @Override public @Nullable SqlNode getCastSpec(RelDataType type) {
-    if (type instanceof BasicSqlType) {
-      switch (type.getSqlTypeName()) {
-      case INTEGER:
-        return new SqlDataTypeSpec(
-            new SqlAlienSystemTypeNameSpec(
-                "INT",
-                type.getSqlTypeName(),
-                SqlParserPos.ZERO),
-            SqlParserPos.ZERO);
-      case REAL:
-        return new SqlDataTypeSpec(
-            new SqlAlienSystemTypeNameSpec("FLOAT", type.getSqlTypeName(),
-                SqlParserPos.ZERO),
-            SqlParserPos.ZERO);
-      case BIGINT:
-        return new SqlDataTypeSpec(
-            new SqlBasicTypeNameSpec(SqlTypeName.BIGINT, SqlParserPos.ZERO),
-            SqlParserPos.ZERO);
-      case TIMESTAMP:
-        return new SqlDataTypeSpec(
-            new SqlAlienSystemTypeNameSpec(
-                "DATETIME",
-                type.getSqlTypeName(),
-                SqlParserPos.ZERO),
-            SqlParserPos.ZERO);
-      case VARCHAR:
-        return new SqlDataTypeSpec(
-            new SqlBasicTypeNameSpec(SqlTypeName.VARCHAR, type.getPrecision(), 
SqlParserPos.ZERO),
-            SqlParserPos.ZERO);
-      default:
-        break;
-      }
-    }
-
-    if (type instanceof AbstractSqlType) {
-      switch (type.getSqlTypeName()) {
-      case MAP:
-        MapSqlType mapSqlType = (MapSqlType) type;
-        SqlDataTypeSpec keySpec = (SqlDataTypeSpec) 
getCastSpec(mapSqlType.getKeyType());
-        SqlDataTypeSpec valueSpec =
-            (SqlDataTypeSpec) getCastSpec(mapSqlType.getValueType());
-        SqlDataTypeSpec nonNullKeySpec =
-            requireNonNull(keySpec, "keySpec");
-        SqlDataTypeSpec nonNullValueSpec =
-            requireNonNull(valueSpec, "valueSpec");
-        SqlMapTypeNameSpec sqlMapTypeNameSpec =
-            new SqlMapTypeNameSpec(nonNullKeySpec, nonNullValueSpec, 
SqlParserPos.ZERO);
-        return new SqlDataTypeSpec(sqlMapTypeNameSpec,
-            SqlParserPos.ZERO);
-      case ARRAY:
-        ArraySqlType arraySqlType = (ArraySqlType) type;
-        SqlDataTypeSpec arrayValueSpec =
-            (SqlDataTypeSpec) getCastSpec(arraySqlType.getComponentType());
-        SqlDataTypeSpec nonNullarrayValueSpec =
-            requireNonNull(arrayValueSpec, "arrayValueSpec");
-        SqlTypeNameSpec typeNameSpec =
-            new SqlArrayWithAngleBracketsNameSpec(
-                nonNullarrayValueSpec.getTypeNameSpec(),
-                arraySqlType.getSqlTypeName(), SqlParserPos.ZERO);
-        return new SqlDataTypeSpec(typeNameSpec, SqlParserPos.ZERO);
-      case MULTISET:
-        throw new UnsupportedOperationException("StarRocks dialect does not 
support cast to "
-            + type.getSqlTypeName());
-      default:
-        break;
-      }
+    switch (type.getSqlTypeName()) {
+    case INTEGER:
+      return new SqlDataTypeSpec(
+          new SqlAlienSystemTypeNameSpec(
+              "INT",
+              type.getSqlTypeName(),
+              SqlParserPos.ZERO),
+          SqlParserPos.ZERO);
+    case REAL:
+      return new SqlDataTypeSpec(
+          new SqlAlienSystemTypeNameSpec("FLOAT", type.getSqlTypeName(),
+              SqlParserPos.ZERO),
+          SqlParserPos.ZERO);
+    case BIGINT:
+      return new SqlDataTypeSpec(
+          new SqlBasicTypeNameSpec(SqlTypeName.BIGINT, SqlParserPos.ZERO),
+          SqlParserPos.ZERO);
+    case TIMESTAMP:
+      return new SqlDataTypeSpec(
+          new SqlAlienSystemTypeNameSpec(
+              "DATETIME",
+              type.getSqlTypeName(),
+              SqlParserPos.ZERO),
+          SqlParserPos.ZERO);
+    case VARCHAR:
+      return new SqlDataTypeSpec(
+          new SqlBasicTypeNameSpec(SqlTypeName.VARCHAR, type.getPrecision(), 
SqlParserPos.ZERO),
+          SqlParserPos.ZERO);
+    case MAP:
+      MapSqlType mapSqlType = (MapSqlType) type;
+      SqlDataTypeSpec keySpec = (SqlDataTypeSpec) 
getCastSpec(mapSqlType.getKeyType());
+      SqlDataTypeSpec valueSpec =
+          (SqlDataTypeSpec) getCastSpec(mapSqlType.getValueType());
+      SqlDataTypeSpec nonNullKeySpec =
+          requireNonNull(keySpec, "keySpec");
+      SqlDataTypeSpec nonNullValueSpec =
+          requireNonNull(valueSpec, "valueSpec");
+      SqlMapTypeNameSpec sqlMapTypeNameSpec =
+          new SqlMapTypeNameSpec(nonNullKeySpec, nonNullValueSpec, 
SqlParserPos.ZERO);
+      return new SqlDataTypeSpec(sqlMapTypeNameSpec,
+          SqlParserPos.ZERO);
+    case ARRAY:
+      return RelToSqlConverterUtil.getCastSpecAngleBracketArrayType(this, type,
+          SqlParserPos.ZERO);
+    case MULTISET:
+      throw new UnsupportedOperationException("StarRocks dialect does not 
support cast to "
+          + type.getSqlTypeName());
+    default:
+      break;
     }
 
     return super.getCastSpec(type);
diff --git 
a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java 
b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
index b165448615..ddb37e8dac 100644
--- a/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
+++ b/core/src/main/java/org/apache/calcite/util/RelToSqlConverterUtil.java
@@ -21,6 +21,7 @@
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.SqlArrayWithAngleBracketsNameSpec;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlCharStringLiteral;
 import org.apache.calcite.sql.SqlCollectionTypeNameSpec;
@@ -373,6 +374,22 @@ public static SqlDataTypeSpec 
getCastSpecClickHouseSqlArrayType(SqlDialect diale
     return new SqlDataTypeSpec(sqlArrayTypeNameSpec, SqlParserPos.ZERO);
   }
 
+  /**
+   * Transformation ARRAY type from {@code VARCHAR ARRAY} to {@code 
Array<VARCHAR>}.
+   */
+  public static SqlDataTypeSpec getCastSpecAngleBracketArrayType(SqlDialect 
dialect,
+      RelDataType type, SqlParserPos pos) {
+    ArraySqlType arraySqlType = (ArraySqlType) type;
+    SqlDataTypeSpec arrayValueSpec =
+        (SqlDataTypeSpec) dialect.getCastSpec(arraySqlType.getComponentType());
+    SqlDataTypeSpec nonNullarrayValueSpec =
+        requireNonNull(arrayValueSpec, "arrayValueSpec");
+    SqlArrayWithAngleBracketsNameSpec sqlArrayTypeNameSpec =
+        new 
SqlArrayWithAngleBracketsNameSpec(nonNullarrayValueSpec.getTypeNameSpec(),
+            arraySqlType.getSqlTypeName(), pos);
+    return new SqlDataTypeSpec(sqlArrayTypeNameSpec, SqlParserPos.ZERO);
+  }
+
   /**
    * ClickHouseSqlMapTypeNameSpec to parse or unparse SQL MAP type to {@code 
Map(VARCHAR, VARCHAR)}.
    */
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 7283f43870..a0b7c2425d 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -2604,6 +2604,27 @@ private SqlDialect nonOrdinalDialect() {
     sql(query4).withCalcite().ok(expected4);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7114";>[CALCITE-7114]
+   * Invalid unparse for cast to array type in Spark</a>.
+   */
+  @Test void testCastArraySpark() {
+    final String query = "select cast(array['a','b','c']"
+        + " as varchar array)";
+    final String expectedSpark = "SELECT CAST(ARRAY ('a', 'b', 'c') AS ARRAY< 
STRING >)\n"
+        + "FROM (VALUES (0)) `t` (`ZERO`)";
+    sql(query)
+        .withSpark().ok(expectedSpark);
+
+    final String query1 = "select cast(array[array['a'], array['b'], 
array['c']]"
+        + " as varchar array array)";
+    final String expectedSpark1 =
+        "SELECT CAST(ARRAY (ARRAY ('a'), ARRAY ('b'), ARRAY ('c')) AS ARRAY< 
ARRAY< STRING > >)\n"
+            + "FROM (VALUES (0)) `t` (`ZERO`)";
+    sql(query1)
+        .withSpark().ok(expectedSpark1);
+  }
+
   /** Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7055";>[CALCITE-7055]
    * Invalid unparse for cast to array type in StarRocks</a>.
@@ -10301,6 +10322,7 @@ private void checkLiteral2(String expression, String 
expected) {
         .withPhoenix().throws_("Phoenix dialect does not support cast to 
MULTISET")
         .withStarRocks().throws_("StarRocks dialect does not support cast to 
MULTISET")
         .withClickHouse().throws_("ClickHouse dialect does not support cast to 
MULTISET")
+        .withSpark().throws_("Spark dialect does not support cast to MULTISET")
         .withHive().throws_("Hive dialect does not support cast to MULTISET");
 
     String query3 = "SELECT CAST(MAP[1.0,2.0,3.0,4.0] AS MAP<FLOAT, REAL>) 
FROM \"employee\"";

Reply via email to