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

mihaibudiu 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 17215b3696 [CALCITE-7546] NullPointerException in SqlToRelConverter 
for UNNEST(array) AS alias under conformance with allowAliasUnnestItems=true
17215b3696 is described below

commit 17215b36963ef343f3c144d4230db010e19e2df4
Author: Takaaki Nakama <[email protected]>
AuthorDate: Thu Jun 25 15:39:59 2026 +0900

    [CALCITE-7546] NullPointerException in SqlToRelConverter for UNNEST(array) 
AS alias under conformance with allowAliasUnnestItems=true
    
    Under a SqlConformance where allowAliasUnnestItems() is true
    (SqlConformanceEnum.PRESTO and user conformances overriding the flag),
    the AS branch in convertFrom passes fieldNames=null to convertUnnest when
    the AS clause omits a column list. The PRESTO-only branch then called
    requireNonNull(fieldNames, "fieldNames") and threw NPE.
    
    Fall back to default item aliases derived from
    SqlUtil#deriveAliasFromOrdinal, matching the names that
    SqlUnnestOperator#inferReturnType uses during validation. This keeps the
    relational row type aligned with the validator's underlying namespace so
    both struct and scalar element types resolve correctly.
    
    Add tests for struct array, scalar array, and the exact array literal
    reproduction from the issue ("SELECT t FROM UNNEST(ARRAY[1, 2, 3]) AS t").
---
 .../apache/calcite/sql2rel/SqlToRelConverter.java  | 13 +++++-
 .../apache/calcite/test/SqlToRelConverterTest.java | 33 +++++++++++++++
 .../apache/calcite/test/SqlToRelConverterTest.xml  | 48 ++++++++++++++++++++++
 3 files changed, 93 insertions(+), 1 deletion(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 507c144288..ed6d52491a 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -2857,10 +2857,21 @@ private void convertUnnest(Blackboard bb, SqlCall call, 
@Nullable List<String> f
     RelNode uncollect;
     try {
       if (validator().config().conformance().allowAliasUnnestItems()) {
+        // Without an AS column list, mirror SqlUnnestOperator#inferReturnType
+        // so Uncollect's row type stays aligned with the validator.
+        List<String> itemAliases;
+        if (fieldNames != null) {
+          itemAliases = fieldNames;
+        } else {
+          itemAliases = new ArrayList<>(nodes.size());
+          for (int i = 0; i < nodes.size(); i++) {
+            itemAliases.add(SqlUtil.deriveAliasFromOrdinal(i));
+          }
+        }
         uncollect = relBuilder
             .push(child)
             .project(exprs)
-            .uncollect(requireNonNull(fieldNames, "fieldNames"), 
operator.withOrdinality)
+            .uncollect(itemAliases, operator.withOrdinality)
             .build();
       } else {
         // REVIEW danny 2020-04-26: should we unify the normal field aliases 
and
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 c9f409f8c3..1d5833c516 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -1967,6 +1967,39 @@ public static void checkActualAndReferenceFiles() {
     sql(sql).withConformance(SqlConformanceEnum.BIG_QUERY).ok();
   }
 
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7546";>[CALCITE-7546]
+   * NullPointerException in SqlToRelConverter for UNNEST(array) AS alias under
+   * conformance with allowAliasUnnestItems=true</a>.
+   */
+  @Test void testAliasUnnestArrayPlanWithoutColumnList() {
+    final String sql = "select d.deptno, e.empno\n"
+        + "from dept_nested_expanded as d,\n"
+        + " UNNEST(d.employees) as e";
+    sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok();
+  }
+
+  @Test void testAliasUnnestScalarArrayPlanWithoutColumnList() {
+    final String sql = "select d.deptno, a\n"
+        + "from dept_nested_expanded as d,\n"
+        + " UNNEST(d.admins) as a";
+    sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok();
+  }
+
+  /**
+   * Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7546";>[CALCITE-7546]
+   * NullPointerException in SqlToRelConverter for UNNEST(array) AS alias under
+   * conformance with allowAliasUnnestItems=true</a>, using the exact array
+   * literal reproduction from the issue.
+   */
+  @Test void testAliasUnnestArrayLiteralPlanWithoutColumnList() {
+    final String sql = "select t\n"
+        + "from UNNEST(ARRAY[1, 2, 3]) as t";
+    sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok();
+  }
+
   @Test void testArrayOfRecord() {
     sql("select employees[1].detail.skills[2+3].desc from dept_nested").ok();
   }
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 b5361cbbd3..90b3c09a24 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -325,6 +325,20 @@ LogicalProject(A=[$0], B=[$1], C=[$2], DEPTNO=[$3], 
NAME=[$4])
         LogicalProject(A=[$2], B=[$1], C=[$0])
           LogicalValues(tuples=[[{ 1, 2, 3 }]])
         LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testAliasUnnestArrayLiteralPlanWithoutColumnList">
+    <Resource name="sql">
+      <![CDATA[select t
+from UNNEST(ARRAY[1, 2, 3]) as t]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(T=[$0])
+  Uncollect
+    LogicalProject(EXPR$0=[ARRAY(1, 2, 3)])
+      LogicalValues(tuples=[[{ 0 }]])
 ]]>
     </Resource>
   </TestCase>
@@ -396,6 +410,40 @@ from dept_nested_expanded as d,
  UNNEST(d.employees) as t(employee)]]>
     </Resource>
   </TestCase>
+  <TestCase name="testAliasUnnestArrayPlanWithoutColumnList">
+    <Resource name="sql">
+      <![CDATA[select d.deptno, e.empno
+from dept_nested_expanded as d,
+ UNNEST(d.employees) as e]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[$0], EMPNO=[$5.EMPNO])
+  LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{2}])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED_EXPANDED]])
+    Uncollect
+      LogicalProject(EMPLOYEES=[$cor0.EMPLOYEES])
+        LogicalValues(tuples=[[{ 0 }]])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testAliasUnnestScalarArrayPlanWithoutColumnList">
+    <Resource name="sql">
+      <![CDATA[select d.deptno, a
+from dept_nested_expanded as d,
+ UNNEST(d.admins) as a]]>
+    </Resource>
+    <Resource name="plan">
+      <![CDATA[
+LogicalProject(DEPTNO=[$0], A=[$5])
+  LogicalCorrelate(correlation=[$cor0], joinType=[inner], 
requiredColumns=[{3}])
+    LogicalTableScan(table=[[CATALOG, SALES, DEPT_NESTED_EXPANDED]])
+    Uncollect
+      LogicalProject(ADMINS=[$cor0.ADMINS])
+        LogicalValues(tuples=[[{ 0 }]])
+]]>
+    </Resource>
+  </TestCase>
   <TestCase name="testAliasWithinGroupingSets">
     <Resource name="sql">
       <![CDATA[SELECT empno / 2 AS x

Reply via email to