This is an automated email from the ASF dual-hosted git repository. zabetak 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 7b8b2b9604 [CALCITE-4972] Subfields of array columns containing structs are not qualified in getFieldOrigins 7b8b2b9604 is described below commit 7b8b2b96041d0cf7bf69cae336659087739fa495 Author: Mark Grey <mgthesec...@spotify.com> AuthorDate: Fri Jan 7 16:22:10 2022 -0500 [CALCITE-4972] Subfields of array columns containing structs are not qualified in getFieldOrigins Close apache/calcite#2683 --- .../calcite/sql/validate/SqlValidatorImpl.java | 16 +++++++++++++ .../calcite/sql/validate/UnnestNamespace.java | 27 ++++++++++++++++++++++ .../org/apache/calcite/test/SqlValidatorTest.java | 13 +++++++++++ 3 files changed, 56 insertions(+) diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java index c53803e8fd..cd272f649b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java @@ -6145,6 +6145,13 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { scope.fullyQualify((SqlIdentifier) selectItem); SqlValidatorNamespace namespace = requireNonNull(qualified.namespace, () -> "namespace for " + qualified); + if (namespace.isWrapperFor(AliasNamespace.class)) { + AliasNamespace aliasNs = namespace.unwrap(AliasNamespace.class); + SqlNode aliased = requireNonNull(aliasNs.getNode(), () -> + "sqlNode for aliasNs " + aliasNs); + namespace = getNamespaceOrThrow(stripAs(aliased)); + } + final SqlValidatorTable table = namespace.getTable(); if (table == null) { return null; @@ -6152,7 +6159,16 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { final List<String> origin = new ArrayList<>(table.getQualifiedName()); for (String name : qualified.suffix()) { + if (namespace.isWrapperFor(UnnestNamespace.class)) { + // If identifier is drawn from a repeated subrecord via unnest, add name of array field + UnnestNamespace unnestNamespace = namespace.unwrap(UnnestNamespace.class); + final SqlQualified columnUnnestedFrom = unnestNamespace.getColumnUnnestedFrom(name); + if (columnUnnestedFrom != null) { + origin.addAll(columnUnnestedFrom.suffix()); + } + } namespace = namespace.lookupChild(name); + if (namespace == null) { return null; } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java index 531ba53a90..f3e732d993 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/UnnestNamespace.java @@ -64,6 +64,33 @@ class UnnestNamespace extends AbstractNamespace { return null; } + /** + * Given a field name from SelectScope, find the column in this + * UnnestNamespace it originates from. + * + * @param queryFieldName Name of column + * @return A SqlQualified if subfield comes from this unnest, null if not found + */ + @Nullable SqlQualified getColumnUnnestedFrom(String queryFieldName) { + for (SqlNode operand : unnest.getOperandList()) { + // Ignore operands that are inline ARRAY[] literals + if (operand instanceof SqlIdentifier) { + final SqlIdentifier id = (SqlIdentifier) operand; + final SqlQualified qualified = this.scope.fullyQualify(id); + RelDataType dataType = this.scope.resolveColumn(qualified.suffix().get(0), id); + if (dataType != null) { + RelDataType repeatedEntryType = dataType.getComponentType(); + if (repeatedEntryType != null + && repeatedEntryType.isStruct() + && repeatedEntryType.getFieldNames().contains(queryFieldName)) { + return qualified; + } + } + } + } + return null; + } + @Override protected RelDataType validateImpl(RelDataType targetRowType) { // Validate the call and its arguments, and infer the return type. validator.validateCall(unnest, scope); diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index 00dd5e5d7b..be16adca82 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -9210,6 +9210,19 @@ public class SqlValidatorTest extends SqlValidatorTestCase { + " CATALOG.SALES.EMP.HIREDATE," + " null," + " null}")); + + sql("select e.empno from dept_nested, unnest(employees) as e") + .assertFieldOrigin( + is("{CATALOG.SALES.DEPT_NESTED.EMPLOYEES.EMPNO}")); + + sql("select * from UNNEST(ARRAY['a', 'b'])") + .assertFieldOrigin(is("{null}")); + + sql("select * from UNNEST(ARRAY['a', 'b'], ARRAY['d', 'e'])") + .assertFieldOrigin(is("{null, null}")); + + sql("select dpt.skill.desc from dept_nested as dpt") + .assertFieldOrigin(is("{CATALOG.SALES.DEPT_NESTED.SKILL.DESC}")); } @Test void testBrackets() {