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 4622176a073..0f6538bf03e 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -2677,10 +2677,21 @@ private void convertUnnest(Blackboard bb, SqlCall call, @Nullable List 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 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 ebad0e9450a..c43a524f889 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java @@ -1939,6 +1939,39 @@ public static void checkActualAndReferenceFiles() { sql(sql).withConformance(SqlConformanceEnum.PRESTO).ok(); } + /** + * Test case for + * [CALCITE-7546] + * NullPointerException in SqlToRelConverter for UNNEST(array) AS alias under + * conformance with allowAliasUnnestItems=true. + */ + @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 + * [CALCITE-7546] + * NullPointerException in SqlToRelConverter for UNNEST(array) AS alias under + * conformance with allowAliasUnnestItems=true, 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 56f169629ae..9cd1e4c6c9e 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]]) +]]> + + + + + + + + @@ -362,6 +376,40 @@ from dept_nested_expanded as d, UNNEST(d.employees) as t(employee)]]> + + + + + + + + + + + + + + + +