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 0af5284d9f [CALCITE-7238] Query that creates a ROW value triggers an 
assertion failure in SqlToRelConverte
0af5284d9f is described below

commit 0af5284d9f004fd9e38e2d62abd8c04de004be5c
Author: Mihai Budiu <[email protected]>
AuthorDate: Mon Oct 20 17:26:15 2025 -0700

    [CALCITE-7238] Query that creates a ROW value triggers an assertion failure 
in SqlToRelConverte
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../java/org/apache/calcite/rel/core/Collect.java  |  4 +-
 .../java/org/apache/calcite/rex/RexSubQuery.java   |  6 ++-
 .../apache/calcite/sql/type/SqlTypeTransforms.java | 10 +++--
 .../org/apache/calcite/sql/type/SqlTypeUtil.java   | 48 ++++++++++++++++++++++
 .../apache/calcite/test/SqlToRelConverterTest.java |  7 ++++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 16 ++++++++
 6 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/rel/core/Collect.java 
b/core/src/main/java/org/apache/calcite/rel/core/Collect.java
index 88e2cc7927..c308bfe6bf 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Collect.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Collect.java
@@ -153,7 +153,9 @@ public static Collect create(RelNode input,
       rowType =
           deriveRowType(input.getCluster().getTypeFactory(), collectionType,
               fieldName,
-              SqlTypeUtil.deriveCollectionQueryComponentType(collectionType,
+              SqlTypeUtil.deriveCollectionQueryComponentType(
+                  input.getCluster().getTypeFactory(),
+                  collectionType,
                   input.getRowType()));
       break;
     default:
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java 
b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
index 61d10174f8..5a9cad2a97 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSubQuery.java
@@ -126,7 +126,8 @@ public static RexSubQuery array(RelNode rel) {
     final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory();
     final RelDataType type =
         typeFactory.createArrayType(
-            SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.ARRAY, 
rel.getRowType()),
+            SqlTypeUtil.deriveCollectionQueryComponentType(
+                rel.getCluster().getTypeFactory(), SqlTypeName.ARRAY, 
rel.getRowType()),
             -1L);
     return new RexSubQuery(type, SqlStdOperatorTable.ARRAY_QUERY,
         ImmutableList.of(), rel);
@@ -137,7 +138,8 @@ public static RexSubQuery multiset(RelNode rel) {
     final RelDataTypeFactory typeFactory = rel.getCluster().getTypeFactory();
     final RelDataType type =
         typeFactory.createMultisetType(
-            
SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.MULTISET, 
rel.getRowType()),
+            SqlTypeUtil.deriveCollectionQueryComponentType(
+                rel.getCluster().getTypeFactory(), SqlTypeName.MULTISET, 
rel.getRowType()),
             -1L);
     return new RexSubQuery(type, SqlStdOperatorTable.MULTISET_QUERY,
         ImmutableList.of(), rel);
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 f1a699e9f3..253b93b3a0 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
@@ -239,7 +239,9 @@ private SqlTypeName toVar(RelDataType type) {
   public static final SqlTypeTransform TO_MULTISET_QUERY =
       (opBinding, typeToTransform) ->
           TO_MULTISET.transformType(opBinding,
-              
SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.MULTISET,
+              SqlTypeUtil.deriveCollectionQueryComponentType(
+                  opBinding.getTypeFactory(),
+                  SqlTypeName.MULTISET,
                   typeToTransform));
 
   /**
@@ -296,7 +298,8 @@ private SqlTypeName toVar(RelDataType type) {
   public static final SqlTypeTransform TO_ARRAY_QUERY =
       (opBinding, typeToTransform) ->
         TO_ARRAY.transformType(opBinding,
-            SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.ARRAY, 
typeToTransform));
+            SqlTypeUtil.deriveCollectionQueryComponentType(
+                opBinding.getTypeFactory(), SqlTypeName.ARRAY, 
typeToTransform));
 
   /**
    * Parameter type-inference transform strategy that converts a two-field
@@ -318,7 +321,8 @@ private SqlTypeName toVar(RelDataType type) {
   public static final SqlTypeTransform TO_MAP_QUERY =
       (opBinding, typeToTransform) ->
           TO_MAP.transformType(opBinding,
-              SqlTypeUtil.deriveCollectionQueryComponentType(SqlTypeName.MAP, 
typeToTransform));
+              SqlTypeUtil.deriveCollectionQueryComponentType(
+                  opBinding.getTypeFactory(), SqlTypeName.MAP, 
typeToTransform));
 
   /**
    * Parameter type-inference transform strategy that converts a type to a MAP 
type,
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java 
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
index 42d6bc9edb..122708a41b 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeUtil.java
@@ -39,6 +39,7 @@
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.util.NumberUtil;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
@@ -178,12 +179,59 @@ public static boolean isCharTypeComparable(
     return true;
   }
 
+  /** A namespace may contain multiple fields with the same name.
+   * However, a proper ROW type cannot; this function will assign
+   * unique names to fields when they are used to build a concrete ROW type.
+   *
+   * @param type A type for which type.isStruct() is true.
+   * @return A new version of this type where all fields have unique names.
+   *
+   * <p>Note: the same rule to rename fields is used by the {@link 
SqlToRelConverter} later,
+   * in convertNonAggregateSelectList (a private method).  This ensures that
+   * the generated field names there will match with the inferred field names 
here.
+   */
+  private static RelDataType uniquify(RelDataTypeFactory factory, RelDataType 
type) {
+    List<String> unique = SqlValidatorUtil.uniquify(type.getFieldNames(), 
true);
+    List<RelDataType> types = type.getFieldList()
+        .stream()
+        .map(RelDataTypeField::getType)
+        .collect(Collectors.toList());
+    return factory.createStructType(type.getStructKind(), types, unique);
+  }
+
+  /**
+   * Derives component type for ARRAY, MULTISET, MAP when input is sub-query.
+   *
+   * @param factory Type factory used to generate new types if necessary
+   * @param origin original component type
+   * @return component type
+   */
+  public static RelDataType deriveCollectionQueryComponentType(
+      RelDataTypeFactory factory,
+      SqlTypeName collectionType,
+      RelDataType origin) {
+    switch (collectionType) {
+    case ARRAY:
+    case MULTISET:
+      return origin.isStruct() && origin.getFieldCount() == 1
+          ? origin.getFieldList().get(0).getType() : uniquify(factory, origin);
+    case MAP:
+      return origin;
+    default:
+      throw new AssertionError(
+          "Impossible to derive component type for " + collectionType);
+    }
+  }
+
   /**
    * Derives component type for ARRAY, MULTISET, MAP when input is sub-query.
    *
    * @param origin original component type
    * @return component type
+   * @deprecated Use
+   * {@link SqlTypeUtil#deriveCollectionQueryComponentType(RelDataTypeFactory, 
SqlTypeName, RelDataType)}
    */
+  @Deprecated
   public static RelDataType deriveCollectionQueryComponentType(
       SqlTypeName collectionType,
       RelDataType origin) {
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 8f020b6f78..be285b6438 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -5430,6 +5430,13 @@ void checkUserDefinedOrderByOver(NullCollation 
nullCollation) {
     sql(sql).ok();
   }
 
+  /** Test case for <a 
href="https://issues.apache.org/jira/browse/CALCITE-7238";>[CALCITE-7238]
+   * Query that creates a ROW value triggers an assertion failure in 
SqlToRelConverter</a>. */
+  @Test void testArrayRow() {
+    sql("WITH tbl(r) AS (VALUES(1)) SELECT\n"
+        + "ARRAY(SELECT r, r FROM tbl)").ok();
+  }
+
   /**
    * Test case for
    * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4145";>[CALCITE-4145]
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 50e6f27b0b..5b0760b6e8 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -521,6 +521,22 @@ LogicalProject(EXPR$0=[ROW(ITEM(ITEM(ITEM(ITEM($3, 0), 
'detail'), 'skills'), 0).
       <![CDATA[
 LogicalProject(EXPR$0=[ITEM(ITEM($3, 1).DETAIL.SKILLS, +(2, 3)).DESC])
   LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testArrayRow">
+    <Resource name="sql">
+      <![CDATA[WITH tbl(r) AS (VALUES(1)) SELECT
+ARRAY(SELECT r, r FROM tbl)]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1])
+  LogicalJoin(condition=[true], joinType=[inner])
+    LogicalValues(tuples=[[{ 0 }]])
+    Collect(field=[EXPR$0])
+      LogicalProject(R=[$0], R0=[$0])
+        LogicalValues(tuples=[[{ 1 }]])
 ]]>
     </Resource>
   </TestCase>

Reply via email to