From 6c7659f66303e22e983793d69a3e7315534bdbaf Mon Sep 17 00:00:00 2001 From: James Starr Date: Tue, 10 Sep 2024 16:07:50 -0700 Subject: [PATCH] [CALCITE-6574] SqlValidatorImpl Refactor: SqlQueryScopes Adding SqlQueryScopes to encapsulate the data for resolving SqlNode to SqlValidatorScope and SqlValidatorNamespaces. Moving the api for resolving scopes from SqlValidator to SqlQueryScopes. --- .../apache/calcite/sql/SqlCallBinding.java | 2 +- .../calcite/sql/SqlWithinGroupOperator.java | 3 +- .../sql/advise/SqlAdvisorValidator.java | 4 +- .../sql/fun/SqlMultisetQueryConstructor.java | 2 +- .../apache/calcite/sql/type/OperandTypes.java | 4 +- .../calcite/sql/validate/AggChecker.java | 2 +- .../calcite/sql/validate/AliasNamespace.java | 4 +- .../calcite/sql/validate/DelegatingScope.java | 6 +- .../calcite/sql/validate/JoinNamespace.java | 4 +- .../calcite/sql/validate/OrderByScope.java | 7 +- .../calcite/sql/validate/PivotScope.java | 2 +- .../calcite/sql/validate/SelectNamespace.java | 4 +- .../calcite/sql/validate/SetopNamespace.java | 4 +- .../sql/validate/SqlNonNullableAccessors.java | 2 +- .../calcite/sql/validate/SqlQueryScopes.java | 274 +++++++++ .../sql/validate/SqlQueryScopesImpl.java | 343 +++++++++++ .../calcite/sql/validate/SqlValidator.java | 172 +----- .../sql/validate/SqlValidatorImpl.java | 559 +++++------------- .../sql/validate/SqlValidatorNamespace.java | 2 +- .../sql/validate/SqlValidatorScope.java | 3 +- .../calcite/sql/validate/TableScope.java | 2 +- .../calcite/sql/validate/UnpivotScope.java | 2 +- .../sql/validate/WithItemNamespace.java | 2 +- .../calcite/sql/validate/WithNamespace.java | 4 +- .../sql/validate/WithRecursiveScope.java | 4 +- .../calcite/sql/validate/WithScope.java | 4 +- .../implicit/AbstractTypeCoercion.java | 2 +- .../validate/implicit/TypeCoercionImpl.java | 7 +- .../calcite/sql2rel/SqlToRelConverter.java | 50 +- .../apache/calcite/test/SqlOperatorTest.java | 2 +- .../calcite/test/SqlValidatorFixture.java | 2 +- 31 files changed, 843 insertions(+), 641 deletions(-) create mode 100644 core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopes.java create mode 100644 core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopesImpl.java diff --git a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java index bf0bee7743d3..6095d70ae9a5 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlCallBinding.java @@ -355,7 +355,7 @@ public SqlCall permutedCall() { @Override public RelDataType getOperandType(int ordinal) { final SqlNode operand = call.operand(ordinal); final RelDataType type = SqlTypeUtil.deriveType(this, operand); - final SqlValidatorNamespace namespace = validator.getNamespace(operand); + final SqlValidatorNamespace namespace = validator.getSqlQueryScopes().getNamespace(operand); if (namespace != null) { return namespace.getType(); } diff --git a/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java index 8754c4d7cad8..dc6e56f9fbbb 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java @@ -123,7 +123,8 @@ private PercentileDiscCallBinding(SqlValidator validator, @Override public RelDataType getCollationType() { final RelDataType type = SqlTypeUtil.deriveType(this, collationColumn); - final SqlValidatorNamespace namespace = super.getValidator().getNamespace(collationColumn); + final SqlValidatorNamespace namespace = super.getValidator().getSqlQueryScopes() + .getNamespace(collationColumn); return namespace != null ? namespace.getType() : type; } } diff --git a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java index ddcfe9a6a9b6..92bbf9b7b030 100644 --- a/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java +++ b/core/src/main/java/org/apache/calcite/sql/advise/SqlAdvisorValidator.java @@ -169,11 +169,11 @@ private void registerId(SqlIdentifier id, SqlValidatorScope scope) { @Override protected void validateOver(SqlCall call, SqlValidatorScope scope) { try { - final OverScope overScope = (OverScope) getOverScope(call); + final OverScope overScope = (OverScope) getSqlQueryScopes().getOverScope(call); final SqlNode relation = call.operand(0); validateFrom(relation, unknownType, scope); final SqlNode window = call.operand(1); - SqlValidatorScope opScope = scopes.get(relation); + SqlValidatorScope opScope = getSqlQueryScopes().getScope(relation); if (opScope == null) { opScope = overScope; } diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java index 05ed42ca8427..b14a81a84db1 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMultisetQueryConstructor.java @@ -86,7 +86,7 @@ protected SqlMultisetQueryConstructor(String name, SqlKind kind, SqlSelect subSelect = call.operand(0); subSelect.validateExpr(validator, scope); final SqlValidatorNamespace ns = - requireNonNull(validator.getNamespace(subSelect), + requireNonNull(validator.getSqlQueryScopes().getNamespace(subSelect), () -> "namespace is missing for " + subSelect); final RelDataType rowType = requireNonNull(ns.getRowType(), "rowType"); final SqlCallBinding opBinding = new SqlCallBinding(validator, scope, call); diff --git a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java index a9544ca463ec..b664a904b309 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java +++ b/core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java @@ -1732,7 +1732,7 @@ private static class LambdaFamilyOperandTypeChecker && !argFamilies.stream().allMatch(f -> f == SqlTypeFamily.ANY)) { // Replace the parameter types in the lambda expression. final SqlLambdaScope scope = - (SqlLambdaScope) validator.getLambdaScope(lambdaExpr); + (SqlLambdaScope) validator.getSqlQueryScopes().getLambdaScope(lambdaExpr); for (int i = 0; i < argFamilies.size(); i++) { final SqlNode param = lambdaExpr.getParameters().get(i); final RelDataType type = @@ -1794,7 +1794,7 @@ private static class LambdaRelOperandTypeChecker // Replace the parameter types in the lambda expression. final SqlValidator validator = callBinding.getValidator(); final SqlLambdaScope scope = - (SqlLambdaScope) validator.getLambdaScope(lambdaExpr); + (SqlLambdaScope) validator.getSqlQueryScopes().getLambdaScope(lambdaExpr); for (int i = 0; i < argTypes.size(); i++) { final SqlNode param = lambdaExpr.getParameters().get(i); final RelDataType type = argTypes.get(i); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java index 43ccc3453b19..47b6b3187a55 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java @@ -147,7 +147,7 @@ && isMeasureExp(id)) { if (scope instanceof AggregatingSelectScope) { final SqlSelect select = (SqlSelect) scope.getNode(); SelectScope selectScope = - requireNonNull(validator.getRawSelectScope(select), + requireNonNull(validator.getSqlQueryScopes().getRawSelectScope(select), () -> "rawSelectScope for " + scope.getNode()); List selectList = requireNonNull(selectScope.getExpandedSelectList(), diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java index 04296d98172d..d55481bf2f3b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/AliasNamespace.java @@ -74,14 +74,14 @@ protected AliasNamespace( @Override public boolean supportsModality(SqlModality modality) { final List operands = call.getOperandList(); final SqlValidatorNamespace childNs = - validator.getNamespaceOrThrow(operands.get(0)); + validator.getSqlQueryScopes().getNamespaceOrThrow(operands.get(0)); return childNs.supportsModality(modality); } @Override protected RelDataType validateImpl(RelDataType targetRowType) { final List operands = call.getOperandList(); final SqlValidatorNamespace childNs = - validator.getNamespaceOrThrow(operands.get(0)); + validator.getSqlQueryScopes().getNamespaceOrThrow(operands.get(0)); final RelDataType rowType = childNs.getRowTypeSansSystemColumns(); final RelDataType aliasedType; if (operands.size() == 2) { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java index 5153d3160e50..b420a9bee3c5 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/DelegatingScope.java @@ -234,9 +234,9 @@ protected void addColumnNames( @Override public SqlValidatorScope getOperandScope(SqlCall call) { if (call instanceof SqlSelect) { - return validator.getSelectScope((SqlSelect) call); + return validator.getSqlQueryScopes().getSelectScope((SqlSelect) call); } else if (call instanceof SqlLambda) { - return validator.getLambdaScope((SqlLambda) call); + return validator.getSqlQueryScopes().getLambdaScope((SqlLambda) call); } return this; } @@ -673,7 +673,7 @@ public SqlValidatorScope getParent() { return null; case 1: final SqlValidatorNamespace selectNs = - validator.getNamespaceOrThrow(select); + validator.getSqlQueryScopes().getNamespaceOrThrow(select); return SqlQualified.create(this, 1, selectNs, identifier); default: // More than one column has this alias. diff --git a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java index c533796acd20..003d0971c3a4 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/JoinNamespace.java @@ -42,9 +42,9 @@ class JoinNamespace extends AbstractNamespace { @Override protected RelDataType validateImpl(RelDataType targetRowType) { RelDataType leftType = - validator.getNamespaceOrThrow(join.getLeft()).getRowType(); + validator.getSqlQueryScopes().getNamespaceOrThrow(join.getLeft()).getRowType(); RelDataType rightType = - validator.getNamespaceOrThrow(join.getRight()).getRowType(); + validator.getSqlQueryScopes().getNamespaceOrThrow(join.getRight()).getRowType(); final RelDataTypeFactory typeFactory = validator.getTypeFactory(); switch (join.getJoinType()) { case LEFT: diff --git a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java index b8e58222ad97..b3eaf7af815b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/OrderByScope.java @@ -63,7 +63,7 @@ public class OrderByScope extends DelegatingScope { } @Override public void findAllColumnNames(List result) { - final SqlValidatorNamespace ns = validator.getNamespaceOrThrow(select); + final SqlValidatorNamespace ns = validator.getSqlQueryScopes().getNamespaceOrThrow(select); addColumnNames(ns, result); } @@ -80,14 +80,15 @@ public class OrderByScope extends DelegatingScope { } @Override public @Nullable RelDataType resolveColumn(String name, SqlNode ctx) { - final SqlValidatorNamespace selectNs = validator.getNamespaceOrThrow(select); + final SqlValidatorNamespace selectNs = + validator.getSqlQueryScopes().getNamespaceOrThrow(select); final RelDataType rowType = selectNs.getRowType(); final SqlNameMatcher nameMatcher = validator.catalogReader.nameMatcher(); final RelDataTypeField field = nameMatcher.field(rowType, name); if (field != null) { return field.getType(); } - final SqlValidatorScope selectScope = validator.getSelectScope(select); + final SqlValidatorScope selectScope = validator.getSqlQueryScopes().getSelectScope(select); return selectScope.resolveColumn(name, ctx); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java b/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java index eb0436cbef4b..b688b4352bde 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/PivotScope.java @@ -39,7 +39,7 @@ public PivotScope(SqlValidatorScope parent, SqlPivot pivot) { * scope only has one namespace, and it is anonymous. */ public SqlValidatorNamespace getChild() { return requireNonNull( - validator.getNamespace(pivot.query), + validator.getSqlQueryScopes().getNamespace(pivot.query), () -> "namespace for pivot.query " + pivot.query); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java index 9819d28a584e..0141fb91fb7a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SelectNamespace.java @@ -71,11 +71,11 @@ public SelectNamespace( final RelDataType rowType = this.getRowTypeSansSystemColumns(); final int field = SqlTypeUtil.findField(rowType, columnName); SelectScope selectScope = - requireNonNull(validator.getRawSelectScope(select), + requireNonNull(validator.getSqlQueryScopes().getRawSelectScope(select), () -> "rawSelectScope for " + select); final SqlNode selectItem = requireNonNull(selectScope.getExpandedSelectList(), () -> "expandedSelectList for selectScope of " + select).get(field); - return validator.getSelectScope(select).getMonotonicity(selectItem); + return validator.getSqlQueryScopes().getSelectScope(select).getMonotonicity(selectItem); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java index 58c381e80c31..779604f7bcba 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SetopNamespace.java @@ -67,7 +67,7 @@ protected SetopNamespace( } for (SqlNode operand : call.getOperandList()) { final SqlValidatorNamespace namespace = - requireNonNull(validator.getNamespace(operand), + requireNonNull(validator.getSqlQueryScopes().getNamespace(operand), () -> "namespace for " + operand); monotonicity = combine(monotonicity, @@ -103,7 +103,7 @@ private static SqlMonotonicity combine(@Nullable SqlMonotonicity m0, case INTERSECT: case EXCEPT: final SqlValidatorScope scope = - requireNonNull(validator.scopes.get(call), + requireNonNull(validator.getSqlQueryScopes().getScope(call), () -> "scope for " + call); for (SqlNode operand : call.getOperandList()) { if (!operand.isA(SqlKind.QUERY)) { diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java index 7fbca5f888fc..3d2b1e85243f 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlNonNullableAccessors.java @@ -104,7 +104,7 @@ public static SqlValidatorScope getScope(SqlCallBinding callBinding) { @API(since = "1.27", status = API.Status.EXPERIMENTAL) public static SqlValidatorNamespace getNamespace(SqlCallBinding callBinding) { return requireNonNull( - callBinding.getValidator().getNamespace(callBinding.getCall()), + callBinding.getValidator().getSqlQueryScopes().getNamespace(callBinding.getCall()), () -> "scope is null for " + safeToString(callBinding)); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopes.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopes.java new file mode 100644 index 000000000000..fb644277c590 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopes.java @@ -0,0 +1,274 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.validate; + +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlLambda; +import org.apache.calcite.sql.SqlMatchRecognize; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.util.Util; + +import org.apiguardian.api.API; +import org.checkerframework.checker.nullness.qual.Nullable; + +/** + * Contains mapping of SqlNodes to SqlValidatorScope. + * + *

The methods {@link #getSelectScope}, {@link #getFromScope}, + * {@link #getWhereScope}, {@link #getGroupScope}, {@link #getHavingScope}, + * {@link #getOrderScope} and {@link #getJoinScope} get the correct scope + * to resolve names in a particular clause of a SQL statement. + */ +public interface SqlQueryScopes { + SqlValidatorScope getCursorScope(SqlSelect select); + + /** + * Returns a scope containing the objects visible from the FROM clause of a + * query. + * + * @param select SELECT statement + * @return naming scope for FROM clause + */ + SqlValidatorScope getFromScope(SqlSelect select); + + /** + * Returns a scope containing the objects visible from the GROUP BY clause + * of a query. + * + * @param select SELECT statement + * @return naming scope for GROUP BY clause + */ + SqlValidatorScope getGroupScope(SqlSelect select); + + /** + * Returns a scope containing the objects visible from the HAVING clause of + * a query. + * + * @param select SELECT statement + * @return naming scope for HAVING clause + */ + SqlValidatorScope getHavingScope(SqlSelect select); + + /** + * Returns a scope containing the objects visible from the ON and USING + * sections of a JOIN clause. + * + * @param node The item in the FROM clause which contains the ON or USING + * expression + * @return naming scope for JOIN clause + * @see #getFromScope + */ + SqlValidatorScope getJoinScope(SqlNode node); + + /** + * Returns the lambda expression scope. + * + * @param node Lambda expression + * @return naming scope for lambda expression + */ + SqlValidatorScope getLambdaScope(SqlLambda node); + + /** + * Returns a scope match recognize clause. + * + * @param node Match recognize + * @return naming scope for Match recognize clause + */ + SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node); + + SqlValidatorScope getMeasureScope(SqlSelect select); + + /** + * Returns the scope that expressions in the SELECT and HAVING clause of + * this query should use. This scope consists of the FROM clause and the + * enclosing scope. If the query is aggregating, only columns in the GROUP + * BY clause may be used. + * + * @param select SELECT statement + * @return naming scope for ORDER BY clause + */ + SqlValidatorScope getOrderScope(SqlSelect select); + + /** + * Returns the scope of an OVER or VALUES node. + * + * @param node Node + * @return Scope + */ + SqlValidatorScope getOverScope(SqlNode node); + + /** + * Returns the appropriate scope for validating a particular clause of a + * SELECT statement. + * + *

Consider + * + *

SELECT *
+   * FROM foo
+   * WHERE EXISTS (
+   *    SELECT deptno AS x
+   *    FROM emp
+   *       JOIN dept ON emp.deptno = dept.deptno
+   *    WHERE emp.deptno = 5
+   *    GROUP BY deptno
+   *    ORDER BY x)
+ * + *

What objects can be seen in each part of the sub-query? + * + *

    + *
  • In FROM ({@link #getFromScope} , you can only see 'foo'. + * + *
  • In WHERE ({@link #getWhereScope}), GROUP BY ({@link #getGroupScope}), + * SELECT ({@code getSelectScope}), and the ON clause of the JOIN + * ({@link #getJoinScope}) you can see 'emp', 'dept', and 'foo'. + * + *
  • In ORDER BY ({@link #getOrderScope}), you can see the column alias 'x'; + * and tables 'emp', 'dept', and 'foo'. + * + *
+ * + * @param select SELECT statement + * @return naming scope for SELECT statement + */ + SqlValidatorScope getSelectScope(SqlSelect select); + /** + * Returns the scope that expressions in the WHERE and GROUP BY clause of + * this query should use. This scope consists of the tables in the FROM + * clause, and the enclosing scope. + * + * @param select Query + * @return naming scope of WHERE clause + */ + SqlValidatorScope getWhereScope(SqlSelect select); + SqlValidatorScope getWithScope(SqlNode withItem); + + SqlValidatorScope getScopeOrThrow(SqlNode node); + + @Nullable SqlValidatorScope getScope(SqlNode sqlNode); + + SelectScope getRawSelectScopeNonNull(SqlSelect select); + + @Nullable + TableScope getTableScope(); + + + /** + * Finds the namespace corresponding to a given node. + * + *

For example, in the query SELECT * FROM (SELECT * FROM t), t1 AS + * alias, the both items in the FROM clause have a corresponding + * namespace. + * + * @param node Parse tree node + * @return namespace of node + */ + @Nullable SqlValidatorNamespace getNamespace(SqlNode node); + + /** + * Returns the scope for resolving the SELECT, GROUP BY and HAVING clauses. + * Always a {@link SelectScope}; if this is an aggregation query, the + * {@link AggregatingScope} is stripped away. + * + * @param select SELECT statement + * @return naming scope for SELECT statement, sans any aggregating scope + */ + @Nullable SelectScope getRawSelectScope(SqlSelect select); + + + /** + * Namespace for the given node. + * + * @param node node to compute the namespace for + * @param scope namespace scope + * @return namespace for the given node, never null + * @see #getNamespace(SqlNode) + */ + @API(since = "1.27", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlNode node, SqlValidatorScope scope); + + /** + * Namespace for the given node. + * + * @param id identifier to resolve + * @param scope namespace scope + * @return namespace for the given node, never null + * @see SqlQueryScopesImpl#getNamespace(SqlIdentifier, DelegatingScope) + */ + @API(since = "1.26", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier id, @Nullable DelegatingScope scope); + + /** + * Namespace for the given node. + * + * @param node node to compute the namespace for + * @return namespace for the given node, never null + * @see #getNamespace(SqlNode) + */ + @API(since = "1.27", status = API.Status.INTERNAL) + SqlValidatorNamespace getNamespaceOrThrow(SqlNode node); + + + /** Allows {@link SqlQueryScopesImpl}.clauseScopes to have multiple values per SELECT. */ + enum Clause { + WHERE, + GROUP_BY, + SELECT, + MEASURE, + ORDER, + CURSOR, + HAVING, + QUALIFY; + + /** + * Determines if the extender should replace aliases with expanded values. + * For example: + * + *

{@code
+     * SELECT a + a as twoA
+     * GROUP BY twoA
+     * }
+ * + *

turns into + * + *

{@code
+     * SELECT a + a as twoA
+     * GROUP BY a + a
+     * }
+ * + *

This is determined both by the clause and the config. + * + * @param config The configuration + * @return Whether we should replace the alias with its expanded value + */ + public boolean shouldReplaceAliases(SqlValidator.Config config) { + switch (this) { + case GROUP_BY: + return config.conformance().isGroupByAlias(); + + case HAVING: + return config.conformance().isHavingAlias(); + + case QUALIFY: + return true; + + default: + throw Util.unexpected(this); + } + } + } +} diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopesImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopesImpl.java new file mode 100644 index 000000000000..ccbc713d9395 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlQueryScopesImpl.java @@ -0,0 +1,343 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.calcite.sql.validate; + +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLambda; +import org.apache.calcite.sql.SqlMatchRecognize; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.util.IdPair; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; + +import static org.apache.calcite.sql.SqlUtil.stripAs; + +import static java.util.Objects.requireNonNull; + +/** + * An implementation of SqlQueryScopes with helper methods for building up the mapping. + */ +public class SqlQueryScopesImpl implements SqlQueryScopes { + + private final SqlValidatorCatalogReader catalogReader; + /** + * Maps {@link SqlNode query node} objects to the {@link SqlValidatorScope} + * scope created from them. + */ + protected final IdentityHashMap scopes = + new IdentityHashMap<>(); + + + /** + * Maps a {@link SqlSelect} and a {@link Clause} to the scope used by that + * clause. + */ + private final Map, SqlValidatorScope> + clauseScopes = new HashMap<>(); + + /** + * The name-resolution scope of a LATERAL TABLE clause. + */ + private @Nullable TableScope tableScope = null; + + + /** + * Maps a {@link SqlNode node} to the + * {@link SqlValidatorNamespace namespace} which describes what columns they + * contain. + */ + protected final IdentityHashMap namespaces = + new IdentityHashMap<>(); + + public SqlQueryScopesImpl(SqlValidatorCatalogReader catalogReader) { + this.catalogReader = catalogReader; + } + + @Override public SqlValidatorScope getCursorScope(SqlSelect select) { + return getScope(select, Clause.CURSOR); + } + + public @Nullable SqlValidatorScope putCursorScope( + SqlSelect select, SelectScope selectScope) { + return putScope(select, Clause.CURSOR, selectScope); + } + + @Override public SqlValidatorScope getFromScope(SqlSelect select) { + return requireNonNull(scopes.get(select), + () -> "no scope for " + select); + } + + @Override public SqlValidatorScope getGroupScope(SqlSelect select) { + // Yes, it's the same as getWhereScope + return getScope(select, Clause.WHERE); + } + + + public @Nullable SqlValidatorScope putGroupByScope( + SqlSelect select, SqlValidatorScope sqlValidatorScope) { + // Why is the group by, but the get is where? + return putScope(select, Clause.GROUP_BY, sqlValidatorScope); + } + + + @Override public SqlValidatorScope getHavingScope(SqlSelect select) { + // Yes, it's the same as getSelectScope + return getScope(select, Clause.SELECT); + } + + @Override public SqlValidatorScope getJoinScope(SqlNode node) { + return requireNonNull(scopes.get(stripAs(node)), + () -> "scope for " + node); + } + + @Override public SqlValidatorScope getLambdaScope(SqlLambda node) { + return getScopeOrThrow(node); + } + + @Override public SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node) { + return getScopeOrThrow(node); + } + + @Override public SqlValidatorScope getMeasureScope(SqlSelect select) { + return getScope(select, Clause.MEASURE); + } + + public @Nullable SqlValidatorScope putMeasureScope( + SqlSelect select, SqlValidatorScope sqlValidatorScope) { + return putScope(select, Clause.MEASURE, sqlValidatorScope); + } + + @Override public SqlValidatorScope getOrderScope(SqlSelect select) { + return getScope(select, Clause.ORDER); + } + + public @Nullable SqlValidatorScope putOrderScope( + SqlSelect select, SqlValidatorScope sqlValidatorScope) { + return putScope(select, Clause.ORDER, sqlValidatorScope); + } + + @Override public SqlValidatorScope getOverScope(SqlNode node) { + return getScopeOrThrow(node); + } + + @Override public SqlValidatorScope getSelectScope(SqlSelect select) { + return getScope(select, Clause.SELECT); + } + + public @Nullable SqlValidatorScope putSelectScope( + SqlSelect select, SqlValidatorScope sqlValidatorScope) { + return putScope(select, Clause.SELECT, sqlValidatorScope); + } + + @Override public SqlValidatorScope getWhereScope(SqlSelect select) { + return getScope(select, Clause.WHERE); + } + + public @Nullable SqlValidatorScope putWhereScope( + SqlSelect sqlNode, SelectScope selectScope) { + return putScope(sqlNode, Clause.WHERE, selectScope); + } + + @Override public SqlValidatorScope getWithScope(SqlNode withItem) { + assert withItem.getKind() == SqlKind.WITH_ITEM; + return getScopeOrThrow(withItem); + } + + @Override public SqlValidatorScope getScopeOrThrow(SqlNode node) { + return requireNonNull(scopes.get(node), () -> "scope for " + node); + } + + @Override public @Nullable SqlValidatorScope getScope(SqlNode sqlNode) { + return scopes.get(sqlNode); + } + + public @Nullable SqlValidatorScope putScope(SqlNode sqlNode, + SqlValidatorScope sqlValidatorScope) { + return scopes.put(sqlNode, sqlValidatorScope); + } + + public @Nullable SqlValidatorScope putIfAbsent(SqlNode sqlNode, + SqlValidatorScope sqlValidatorScope) { + return scopes.putIfAbsent(sqlNode, sqlValidatorScope); + } + + private @Nullable SqlValidatorScope putScope(SqlSelect select, Clause clause, + SqlValidatorScope sqlValidatorScope) { + return clauseScopes.put(IdPair.of(select, clause), sqlValidatorScope); + } + + @Override public @Nullable SelectScope getRawSelectScope(SqlSelect select) { + SqlValidatorScope scope = clauseScopes.get(IdPair.of(select, Clause.SELECT)); + if (scope instanceof AggregatingSelectScope) { + scope = ((AggregatingSelectScope) scope).getParent(); + } + return (SelectScope) scope; + } + + @Override public SelectScope getRawSelectScopeNonNull(SqlSelect select) { + return requireNonNull(getRawSelectScope(select), + () -> "getRawSelectScope for " + select); + } + + @Override public @Nullable TableScope getTableScope() { + return tableScope; + } + + public void setTableScope(@Nullable TableScope tableScope) { + this.tableScope = tableScope; + } + + + /** + * Registers a new namespace, and adds it as a child of its parent scope. + * Derived class can override this method to tinker with namespaces as they + * are created. + * + * @param usingScope Parent scope (which will want to look for things in + * this namespace) + * @param alias Alias by which parent will refer to this namespace + * @param ns Namespace + * @param forceNullable Whether to force the type of namespace to be nullable + */ + public void registerNamespace( + @Nullable SqlValidatorScope usingScope, + @Nullable String alias, + SqlValidatorNamespace ns, + boolean forceNullable) { + SqlValidatorNamespace namespace = + namespaces.get(requireNonNull(ns.getNode(), () -> "ns.getNode() for " + ns)); + if (namespace == null) { + namespaces.put(requireNonNull(ns.getNode()), ns); + namespace = ns; + } + if (usingScope != null) { + if (alias == null) { + throw new IllegalArgumentException("Registering namespace " + ns + + ", into scope " + usingScope + ", so alias must not be null"); + } + usingScope.addChild(namespace, alias, forceNullable); + } + } + + + @Override public @Nullable SqlValidatorNamespace getNamespace(SqlNode node) { + switch (node.getKind()) { + case AS: + + // AS has a namespace if it has a column list 'AS t (c1, c2, ...)' + final SqlValidatorNamespace ns = namespaces.get(node); + if (ns != null) { + return ns; + } + // fall through + case TABLE_REF: + case SNAPSHOT: + case OVER: + case COLLECTION_TABLE: + case ORDER_BY: + case TABLESAMPLE: + return getNamespace(((SqlCall) node).operand(0)); + default: + return namespaces.get(node); + } + } + + public @Nullable SqlValidatorNamespace getNamespace(SqlNode node, + SqlValidatorScope scope) { + if (node instanceof SqlIdentifier && scope instanceof DelegatingScope) { + final SqlIdentifier id = (SqlIdentifier) node; + final DelegatingScope idScope = + (DelegatingScope) ((DelegatingScope) scope).getParent(); + return getNamespace(id, idScope); + } else if (node instanceof SqlCall) { + // Handle extended identifiers. + final SqlCall call = (SqlCall) node; + switch (call.getOperator().getKind()) { + case TABLE_REF: + return getNamespace(call.operand(0), scope); + case EXTEND: + final SqlNode operand0 = call.getOperandList().get(0); + final SqlIdentifier identifier = operand0.getKind() == SqlKind.TABLE_REF + ? ((SqlCall) operand0).operand(0) + : (SqlIdentifier) operand0; + final DelegatingScope idScope = (DelegatingScope) scope; + return getNamespace(identifier, idScope); + case AS: + final SqlNode nested = call.getOperandList().get(0); + switch (nested.getKind()) { + case TABLE_REF: + case EXTEND: + return getNamespace(nested, scope); + default: + break; + } + break; + default: + break; + } + } + return getNamespace(node); + } + + public @Nullable SqlValidatorNamespace getNamespace(SqlIdentifier id, + @Nullable DelegatingScope scope) { + if (id.isSimple()) { + final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); + final SqlValidatorScope.ResolvedImpl resolved = + new SqlValidatorScope.ResolvedImpl(); + requireNonNull(scope, () -> "scope needed to lookup " + id) + .resolve(id.names, nameMatcher, false, resolved); + if (resolved.count() == 1) { + return resolved.only().namespace; + } + } + return getNamespace(id); + } + + @Override public SqlValidatorNamespace getNamespaceOrThrow(SqlNode node) { + return requireNonNull( + getNamespace(node), + () -> "namespace for " + node); + } + + @Override public SqlValidatorNamespace getNamespaceOrThrow(SqlNode node, + SqlValidatorScope scope) { + return requireNonNull( + getNamespace(node, scope), + () -> "namespace for " + node + ", scope " + scope); + } + + @Override public SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier id, + @Nullable DelegatingScope scope) { + return requireNonNull( + getNamespace(id, scope), + () -> "namespace for " + id + ", scope " + scope); + } + + private SqlValidatorScope getScope(SqlSelect select, Clause clause) { + return requireNonNull( + clauseScopes.get(IdPair.of(select, clause)), + () -> "no " + clause + " scope for " + select); + } +} diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java index b113dec80f23..2fe033bedc88 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java @@ -35,7 +35,6 @@ import org.apache.calcite.sql.SqlIntervalQualifier; import org.apache.calcite.sql.SqlLambda; import org.apache.calcite.sql.SqlLiteral; -import org.apache.calcite.sql.SqlMatchRecognize; import org.apache.calcite.sql.SqlMerge; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; @@ -59,6 +58,7 @@ import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.function.UnaryOperator; /** @@ -109,12 +109,6 @@ *

The validator builds the map by making a quick scan over the query when * the root {@link SqlNode} is first provided. Thereafter, it supplies the * correct scope or namespace object when it calls validation methods. - * - *

The methods {@link #getSelectScope}, {@link #getFromScope}, - * {@link #getWhereScope}, {@link #getGroupScope}, {@link #getHavingScope}, - * {@link #getOrderScope} and {@link #getJoinScope} get the correct scope - * to resolve - * names in a particular clause of a SQL statement. */ @Value.Enclosing public interface SqlValidator { @@ -136,6 +130,14 @@ public interface SqlValidator { @Pure SqlOperatorTable getOperatorTable(); + /** + * Returns a mapping of sql nodes to scopes. + * + * @return sql query scopes + */ + @Pure + SqlQueryScopes getSqlQueryScopes(); + /** * Validates an expression tree. You can call this method multiple times, * but not reentrantly. @@ -429,17 +431,6 @@ default SqlWindow resolveWindow( return resolveWindow(windowOrRef, scope); }; - /** - * Finds the namespace corresponding to a given node. - * - *

For example, in the query SELECT * FROM (SELECT * FROM t), t1 AS - * alias, the both items in the FROM clause have a corresponding - * namespace. - * - * @param node Parse tree node - * @return namespace of node - */ - @Nullable SqlValidatorNamespace getNamespace(SqlNode node); /** * Derives an alias for an expression. If no alias can be derived, returns @@ -467,18 +458,6 @@ default SqlWindow resolveWindow( SqlNodeList expandStar(SqlNodeList selectList, SqlSelect query, boolean includeSystemVars); - /** - * Returns the scope that expressions in the WHERE and GROUP BY clause of - * this query should use. This scope consists of the tables in the FROM - * clause, and the enclosing scope. - * - * @param select Query - * @return naming scope of WHERE clause - */ - SqlValidatorScope getWhereScope(SqlSelect select); - - SqlValidatorScope getMeasureScope(SqlSelect select); - /** * Returns the type factory used by this validator. * @@ -513,120 +492,10 @@ SqlNodeList expandStar(SqlNodeList selectList, SqlSelect query, */ RelDataType getUnknownType(); - /** - * Returns the appropriate scope for validating a particular clause of a - * SELECT statement. - * - *

Consider - * - *

SELECT *
-   * FROM foo
-   * WHERE EXISTS (
-   *    SELECT deptno AS x
-   *    FROM emp
-   *       JOIN dept ON emp.deptno = dept.deptno
-   *    WHERE emp.deptno = 5
-   *    GROUP BY deptno
-   *    ORDER BY x)
- * - *

What objects can be seen in each part of the sub-query? - * - *

    - *
  • In FROM ({@link #getFromScope} , you can only see 'foo'. - * - *
  • In WHERE ({@link #getWhereScope}), GROUP BY ({@link #getGroupScope}), - * SELECT ({@code getSelectScope}), and the ON clause of the JOIN - * ({@link #getJoinScope}) you can see 'emp', 'dept', and 'foo'. - * - *
  • In ORDER BY ({@link #getOrderScope}), you can see the column alias 'x'; - * and tables 'emp', 'dept', and 'foo'. - * - *
- * - * @param select SELECT statement - * @return naming scope for SELECT statement - */ - SqlValidatorScope getSelectScope(SqlSelect select); - - /** - * Returns the scope for resolving the SELECT, GROUP BY and HAVING clauses. - * Always a {@link SelectScope}; if this is an aggregation query, the - * {@link AggregatingScope} is stripped away. - * - * @param select SELECT statement - * @return naming scope for SELECT statement, sans any aggregating scope - */ - @Nullable SelectScope getRawSelectScope(SqlSelect select); - - /** - * Returns a scope containing the objects visible from the FROM clause of a - * query. - * - * @param select SELECT statement - * @return naming scope for FROM clause - */ - SqlValidatorScope getFromScope(SqlSelect select); - - /** - * Returns a scope containing the objects visible from the ON and USING - * sections of a JOIN clause. - * - * @param node The item in the FROM clause which contains the ON or USING - * expression - * @return naming scope for JOIN clause - * @see #getFromScope - */ - SqlValidatorScope getJoinScope(SqlNode node); - - /** - * Returns a scope containing the objects visible from the GROUP BY clause - * of a query. - * - * @param select SELECT statement - * @return naming scope for GROUP BY clause - */ - SqlValidatorScope getGroupScope(SqlSelect select); - - /** - * Returns a scope containing the objects visible from the HAVING clause of - * a query. - * - * @param select SELECT statement - * @return naming scope for HAVING clause - */ - SqlValidatorScope getHavingScope(SqlSelect select); - - /** - * Returns the scope that expressions in the SELECT and HAVING clause of - * this query should use. This scope consists of the FROM clause and the - * enclosing scope. If the query is aggregating, only columns in the GROUP - * BY clause may be used. - * - * @param select SELECT statement - * @return naming scope for ORDER BY clause - */ - SqlValidatorScope getOrderScope(SqlSelect select); - - /** - * Returns a scope match recognize clause. - * - * @param node Match recognize - * @return naming scope for Match recognize clause - */ - SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node); - - /** - * Returns the lambda expression scope. - * - * @param node Lambda expression - * @return naming scope for lambda expression - */ - SqlValidatorScope getLambdaScope(SqlLambda node); - /** * Returns a scope that cannot see anything. */ - SqlValidatorScope getEmptyScope(); + SqlValidatorScope createEmptyScope(); /** * Declares a SELECT expression as a cursor. @@ -757,14 +626,6 @@ CalciteException handleUnresolvedFunction(SqlCall call, */ RelDataType getParameterRowType(SqlNode sqlQuery); - /** - * Returns the scope of an OVER or VALUES node. - * - * @param node Node - * @return Scope - */ - SqlValidatorScope getOverScope(SqlNode node); - /** * Validates that a query is capable of producing a return of given modality * (relational or streaming). @@ -784,8 +645,6 @@ boolean validateModality(SqlSelect select, SqlModality modality, void validateSequenceValue(SqlValidatorScope scope, SqlIdentifier id); - SqlValidatorScope getWithScope(SqlNode withItem); - /** Get the type coercion instance. */ TypeCoercion getTypeCoercion(); @@ -1014,6 +873,17 @@ default Config withNakedMeasures(boolean nakedMeasures) { return SqlConformanceEnum.DEFAULT; } + /** Set a factory for query scopes to allow for custom behavior downstream. */ + Config withSqlQueryScopeFactory( + Function supplier); + + /** Returns a Factory of SqlQueryScopes. */ + @Value.Default default Function + sqlQueryScopeFactory() { + return SqlQueryScopesImpl::new; + } + + /** Returns the SQL conformance. * * @deprecated Use {@link #conformance()} */ 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 706c8e6be0e5..e13c15179c45 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 @@ -104,10 +104,10 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; -import org.apache.calcite.sql.util.IdPair; import org.apache.calcite.sql.util.SqlBasicVisitor; import org.apache.calcite.sql.util.SqlShuttle; import org.apache.calcite.sql.util.SqlVisitor; +import org.apache.calcite.sql.validate.SqlQueryScopes.Clause; import org.apache.calcite.sql.validate.implicit.TypeCoercion; import org.apache.calcite.util.BitString; import org.apache.calcite.util.Bug; @@ -126,7 +126,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import org.apiguardian.api.API; import org.checkerframework.checker.nullness.qual.KeyFor; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -214,33 +213,6 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { */ protected final Map idPositions = new HashMap<>(); - /** - * Maps {@link SqlNode query node} objects to the {@link SqlValidatorScope} - * scope created from them. - */ - protected final IdentityHashMap scopes = - new IdentityHashMap<>(); - - /** - * Maps a {@link SqlSelect} and a {@link Clause} to the scope used by that - * clause. - */ - private final Map, SqlValidatorScope> - clauseScopes = new HashMap<>(); - - /** - * The name-resolution scope of a LATERAL TABLE clause. - */ - private @Nullable TableScope tableScope = null; - - /** - * Maps a {@link SqlNode node} to the - * {@link SqlValidatorNamespace namespace} which describes what columns they - * contain. - */ - protected final IdentityHashMap namespaces = - new IdentityHashMap<>(); - /** * Set of select expressions used as cursor definitions. In standard SQL, * only the top-level SELECT is a cursor; Calcite extends this with @@ -298,6 +270,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { // TypeCoercion instance used for implicit type coercion. private final TypeCoercion typeCoercion; + private final SqlQueryScopesImpl sqlQueryScopes; //~ Constructors ----------------------------------------------------------- @@ -339,6 +312,7 @@ protected SqlValidatorImpl( @SuppressWarnings("argument.type.incompatible") TypeCoercion typeCoercion = config.typeCoercionFactory().create(typeFactory, this); this.typeCoercion = typeCoercion; + this.sqlQueryScopes = config.sqlQueryScopeFactory().apply(catalogReader); if (config.conformance().allowLenientCoercion()) { final SqlTypeCoercionRule rules = @@ -406,7 +380,7 @@ public SqlConformance getConformance() { list, catalogReader.nameMatcher().createSet(), types, includeSystemVars); } - getRawSelectScopeNonNull(select).setExpandedSelectList(list); + sqlQueryScopes.getRawSelectScopeNonNull(select).setExpandedSelectList(list); return new SqlNodeList(list, SqlParserPos.ZERO); } @@ -426,11 +400,11 @@ public SqlConformance getConformance() { // that is the argument to the cursor constructor; register it // with a scope corresponding to the cursor SelectScope cursorScope = - new SelectScope(parentScope, getEmptyScope(), select); - clauseScopes.put(IdPair.of(select, Clause.CURSOR), cursorScope); + new SelectScope(parentScope, createEmptyScope(), select); + sqlQueryScopes.putCursorScope(select, cursorScope); final SelectNamespace selectNs = createSelectNamespace(select, select); final String alias = SqlValidatorUtil.alias(select, nextGeneratedId++); - registerNamespace(cursorScope, alias, selectNs, false); + sqlQueryScopes.registerNamespace(cursorScope, alias, selectNs, false); } @Override public void pushFunctionCall() { @@ -468,10 +442,10 @@ private boolean expandSelectItem(final SqlNode selectItem, SqlSelect select, final SqlValidatorScope selectScope; SqlNode expanded; if (SqlValidatorUtil.isMeasure(selectItem)) { - selectScope = getMeasureScope(select); + selectScope = sqlQueryScopes.getMeasureScope(select); expanded = selectItem; } else { - final SelectScope scope = (SelectScope) getWhereScope(select); + final SelectScope scope = (SelectScope) sqlQueryScopes.getWhereScope(select); if (expandStar(selectItems, aliases, fields, includeSystemVars, scope, selectItem)) { return true; @@ -480,7 +454,7 @@ private boolean expandSelectItem(final SqlNode selectItem, SqlSelect select, // Expand the select item: fully-qualify columns, and convert // parentheses-free functions such as LOCALTIME into explicit function // calls. - selectScope = getSelectScope(select); + selectScope = sqlQueryScopes.getSelectScope(select); expanded = expandSelectExpr(selectItem, scope, select); } final String alias = @@ -614,8 +588,8 @@ private static Map getFieldAliases(final SelectScope scope) { private List deriveNaturalJoinColumnList(SqlJoin join) { return SqlValidatorUtil.deriveNaturalJoinColumnList( catalogReader.nameMatcher(), - getNamespaceOrThrow(join.getLeft()).getRowType(), - getNamespaceOrThrow(join.getRight()).getRowType()); + sqlQueryScopes.getNamespaceOrThrow(join.getLeft()).getRowType(), + sqlQueryScopes.getNamespaceOrThrow(join.getRight()).getRowType()); } private static SqlNode expandCommonColumn(SqlSelect sqlSelect, @@ -708,7 +682,7 @@ private boolean expandStar(List selectItems, Set aliases, includeSystemVars); } else { final SqlNode from2 = SqlNonNullableAccessors.getNode(child); - final SqlValidatorNamespace fromNs = getNamespaceOrThrow(from2, scope); + final SqlValidatorNamespace fromNs = sqlQueryScopes.getNamespaceOrThrow(from2, scope); final RelDataType rowType = fromNs.getRowType(); for (RelDataTypeField field : rowType.getFieldList()) { String columnName = field.getName(); @@ -868,7 +842,7 @@ private boolean addOrExpandField(List selectItems, Set aliases, null, false); } - final SqlValidatorNamespace ns = getNamespace(outermostNode); + final SqlValidatorNamespace ns = sqlQueryScopes.getNamespace(outermostNode); if (ns == null) { throw new AssertionError("Not a query: " + outermostNode); } @@ -905,7 +879,7 @@ void lookupSelectHints( IdInfo info = idPositions.get(pos.toString()); if (info == null) { SqlNode fromNode = select.getFrom(); - final SqlValidatorScope fromScope = getFromScope(select); + final SqlValidatorScope fromScope = sqlQueryScopes.getFromScope(select); lookupFromHints(fromNode, fromScope, pos, hintList); } else { lookupNameCompletionHints(info.scope, info.id.names, @@ -932,7 +906,7 @@ private void lookupFromHints( // This can happen in cases like "select * _suggest_", so from clause is absent return; } - final SqlValidatorNamespace ns = getNamespaceOrThrow(node); + final SqlValidatorNamespace ns = sqlQueryScopes.getNamespaceOrThrow(node); if (ns.isWrapperFor(IdentifierNamespace.class)) { IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class); final SqlIdentifier id = idNs.getId(); @@ -984,7 +958,7 @@ private void lookupJoinHints( case ON: requireNonNull(condition, () -> "join.getCondition() for " + join) .findValidOptions(this, - getScopeOrThrow(join), + sqlQueryScopes.getScopeOrThrow(join), pos, hintList); return; default: @@ -1155,7 +1129,7 @@ private SqlNode validateScopedExpression( @Override public void validateQuery(SqlNode node, SqlValidatorScope scope, RelDataType targetRowType) { - final SqlValidatorNamespace ns = getNamespaceOrThrow(node, scope); + final SqlValidatorNamespace ns = sqlQueryScopes.getNamespaceOrThrow(node, scope); if (node.getKind() == SqlKind.TABLESAMPLE) { List operands = ((SqlCall) node).getOperandList(); SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands.get(1)); @@ -1235,209 +1209,16 @@ protected void validateNamespace(final SqlValidatorNamespace namespace, } } - @Override public SqlValidatorScope getEmptyScope() { - return new EmptyScope(this); - } - - private SqlValidatorScope getScope(SqlSelect select, Clause clause) { - return requireNonNull( - clauseScopes.get(IdPair.of(select, clause)), - () -> "no " + clause + " scope for " + select); - } - - public SqlValidatorScope getCursorScope(SqlSelect select) { - return getScope(select, Clause.CURSOR); - } - - @Override public SqlValidatorScope getWhereScope(SqlSelect select) { - return getScope(select, Clause.WHERE); - } - - @Override public SqlValidatorScope getSelectScope(SqlSelect select) { - return getScope(select, Clause.SELECT); - } - - @Override public SqlValidatorScope getMeasureScope(SqlSelect select) { - return getScope(select, Clause.MEASURE); - } - - @Override public @Nullable SelectScope getRawSelectScope(SqlSelect select) { - SqlValidatorScope scope = clauseScopes.get(IdPair.of(select, Clause.SELECT)); - if (scope instanceof AggregatingSelectScope) { - scope = ((AggregatingSelectScope) scope).getParent(); - } - return (SelectScope) scope; - } - - private SelectScope getRawSelectScopeNonNull(SqlSelect select) { - return requireNonNull(getRawSelectScope(select), - () -> "getRawSelectScope for " + select); - } - @Override public SqlValidatorScope getHavingScope(SqlSelect select) { - // Yes, it's the same as getSelectScope - return getScope(select, Clause.SELECT); - } - - @Override public SqlValidatorScope getGroupScope(SqlSelect select) { - // Yes, it's the same as getWhereScope - return getScope(select, Clause.WHERE); - } - - @Override public SqlValidatorScope getFromScope(SqlSelect select) { - return requireNonNull(scopes.get(select), - () -> "no scope for " + select); - } - - @Override public SqlValidatorScope getOrderScope(SqlSelect select) { - return getScope(select, Clause.ORDER); - } - - @Override public SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node) { - return getScopeOrThrow(node); - } - - @Override public SqlValidatorScope getLambdaScope(SqlLambda node) { - return getScopeOrThrow(node); - } - - @Override public SqlValidatorScope getJoinScope(SqlNode node) { - return requireNonNull(scopes.get(stripAs(node)), - () -> "scope for " + node); - } - - @Override public SqlValidatorScope getOverScope(SqlNode node) { - return getScopeOrThrow(node); - } - - @Override public SqlValidatorScope getWithScope(SqlNode withItem) { - assert withItem.getKind() == SqlKind.WITH_ITEM; - return getScopeOrThrow(withItem); - } - - private SqlValidatorScope getScopeOrThrow(SqlNode node) { - return requireNonNull(scopes.get(node), () -> "scope for " + node); - } - - private @Nullable SqlValidatorNamespace getNamespace(SqlNode node, - SqlValidatorScope scope) { - if (node instanceof SqlIdentifier && scope instanceof DelegatingScope) { - final SqlIdentifier id = (SqlIdentifier) node; - final DelegatingScope idScope = - (DelegatingScope) ((DelegatingScope) scope).getParent(); - return getNamespace(id, idScope); - } else if (node instanceof SqlCall) { - // Handle extended identifiers. - final SqlCall call = (SqlCall) node; - switch (call.getOperator().getKind()) { - case TABLE_REF: - return getNamespace(call.operand(0), scope); - case EXTEND: - final SqlNode operand0 = call.getOperandList().get(0); - final SqlIdentifier identifier = operand0.getKind() == SqlKind.TABLE_REF - ? ((SqlCall) operand0).operand(0) - : (SqlIdentifier) operand0; - final DelegatingScope idScope = (DelegatingScope) scope; - return getNamespace(identifier, idScope); - case AS: - final SqlNode nested = call.getOperandList().get(0); - switch (nested.getKind()) { - case TABLE_REF: - case EXTEND: - return getNamespace(nested, scope); - default: - break; - } - break; - default: - break; - } - } - return getNamespace(node); - } - - private @Nullable SqlValidatorNamespace getNamespace(SqlIdentifier id, - @Nullable DelegatingScope scope) { - if (id.isSimple()) { - final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); - final SqlValidatorScope.ResolvedImpl resolved = - new SqlValidatorScope.ResolvedImpl(); - requireNonNull(scope, () -> "scope needed to lookup " + id) - .resolve(id.names, nameMatcher, false, resolved); - if (resolved.count() == 1) { - return resolved.only().namespace; - } - } - return getNamespace(id); - } - - @Override public @Nullable SqlValidatorNamespace getNamespace(SqlNode node) { - switch (node.getKind()) { - case AS: - - // AS has a namespace if it has a column list 'AS t (c1, c2, ...)' - final SqlValidatorNamespace ns = namespaces.get(node); - if (ns != null) { - return ns; - } - // fall through - case TABLE_REF: - case SNAPSHOT: - case OVER: - case COLLECTION_TABLE: - case ORDER_BY: - case TABLESAMPLE: - return getNamespace(((SqlCall) node).operand(0)); - default: - return namespaces.get(node); - } + @Override public SqlValidatorScope createEmptyScope() { + return new EmptyScope(this); } - /** - * Namespace for the given node. - * - * @param node node to compute the namespace for - * @return namespace for the given node, never null - * @see #getNamespace(SqlNode) - */ - @API(since = "1.27", status = API.Status.INTERNAL) - SqlValidatorNamespace getNamespaceOrThrow(SqlNode node) { - return requireNonNull( - getNamespace(node), - () -> "namespace for " + node); + @Override public SqlQueryScopes getSqlQueryScopes() { + return sqlQueryScopes; } - /** - * Namespace for the given node. - * - * @param node node to compute the namespace for - * @param scope namespace scope - * @return namespace for the given node, never null - * @see #getNamespace(SqlNode) - */ - @API(since = "1.27", status = API.Status.INTERNAL) - SqlValidatorNamespace getNamespaceOrThrow(SqlNode node, - SqlValidatorScope scope) { - return requireNonNull( - getNamespace(node, scope), - () -> "namespace for " + node + ", scope " + scope); - } - /** - * Namespace for the given node. - * - * @param id identifier to resolve - * @param scope namespace scope - * @return namespace for the given node, never null - * @see #getNamespace(SqlIdentifier, DelegatingScope) - */ - @API(since = "1.26", status = API.Status.INTERNAL) - SqlValidatorNamespace getNamespaceOrThrow(SqlIdentifier id, - @Nullable DelegatingScope scope) { - return requireNonNull( - getNamespace(id, scope), - () -> "namespace for " + id + ", scope " + scope); - } private void handleOffsetFetch(@Nullable SqlNode offset, @Nullable SqlNode fetch) { if (offset instanceof SqlDynamicParam) { @@ -1917,7 +1698,7 @@ protected SqlSelect createSourceSelectForDelete(SqlDelete call) { if (type != null) { return type; } - final SqlValidatorNamespace ns = getNamespace(node); + final SqlValidatorNamespace ns = sqlQueryScopes.getNamespace(node); if (ns != null) { return ns.getType(); } @@ -1989,7 +1770,7 @@ protected SqlSelect createSourceSelectForDelete(SqlDelete call) { if (type != null) { return type; } - final SqlValidatorNamespace ns = getNamespace(expr); + final SqlValidatorNamespace ns = sqlQueryScopes.getNamespace(expr); if (ns != null) { return ns.getType(); } @@ -2103,7 +1884,7 @@ protected void inferUnknownTypes( requireNonNull(inferredType, "inferredType"); requireNonNull(scope, "scope"); requireNonNull(node, "node"); - final SqlValidatorScope newScope = scopes.get(node); + final SqlValidatorScope newScope = sqlQueryScopes.getScope(node); if (newScope != null) { scope = newScope; } @@ -2186,7 +1967,7 @@ protected void inferUnknownTypes( // For MEASURE operator, use the measure scope (which has additional // aliases available) if (scope instanceof SelectScope) { - scope = getMeasureScope(((SelectScope) scope).getNode()); + scope = sqlQueryScopes.getMeasureScope(((SelectScope) scope).getNode()); } inferUnknownTypes(inferredType, scope, ((SqlCall) node).operand(0)); } else if (node instanceof SqlCall) { @@ -2258,11 +2039,11 @@ private void registerMatchRecognize( final MatchRecognizeNamespace matchRecognizeNamespace = createMatchRecognizeNameSpace(call, enclosingNode); - registerNamespace(usingScope, alias, matchRecognizeNamespace, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, alias, matchRecognizeNamespace, forceNullable); final MatchRecognizeScope matchRecognizeScope = new MatchRecognizeScope(parentScope, call); - scopes.put(call, matchRecognizeScope); + sqlQueryScopes.putScope(call, matchRecognizeScope); // parse input query SqlNode expr = call.getTableRef(); @@ -2289,11 +2070,11 @@ private void registerPivot( boolean forceNullable) { final PivotNamespace namespace = createPivotNameSpace(pivot, enclosingNode); - registerNamespace(usingScope, alias, namespace, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, alias, namespace, forceNullable); final SqlValidatorScope scope = new PivotScope(parentScope, pivot); - scopes.put(pivot, scope); + sqlQueryScopes.putScope(pivot, scope); // parse input query SqlNode expr = pivot.query; @@ -2319,11 +2100,11 @@ private void registerUnpivot( boolean forceNullable) { final UnpivotNamespace namespace = createUnpivotNameSpace(call, enclosingNode); - registerNamespace(usingScope, alias, namespace, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, alias, namespace, forceNullable); final SqlValidatorScope scope = new UnpivotScope(parentScope, call); - scopes.put(call, scope); + sqlQueryScopes.putScope(call, scope); // parse input query SqlNode expr = call.query; @@ -2340,36 +2121,6 @@ protected UnpivotNamespace createUnpivotNameSpace(SqlUnpivot call, return new UnpivotNamespace(this, call, enclosingNode); } - /** - * Registers a new namespace, and adds it as a child of its parent scope. - * Derived class can override this method to tinker with namespaces as they - * are created. - * - * @param usingScope Parent scope (which will want to look for things in - * this namespace) - * @param alias Alias by which parent will refer to this namespace - * @param ns Namespace - * @param forceNullable Whether to force the type of namespace to be nullable - */ - protected void registerNamespace( - @Nullable SqlValidatorScope usingScope, - @Nullable String alias, - SqlValidatorNamespace ns, - boolean forceNullable) { - SqlValidatorNamespace namespace = - namespaces.get(requireNonNull(ns.getNode(), () -> "ns.getNode() for " + ns)); - if (namespace == null) { - namespaces.put(requireNonNull(ns.getNode()), ns); - namespace = ns; - } - if (usingScope != null) { - if (alias == null) { - throw new IllegalArgumentException("Registering namespace " + ns - + ", into scope " + usingScope + ", so alias must not be null"); - } - usingScope.addChild(namespace, alias, forceNullable); - } - } /** * Registers scopes and namespaces implied a relational expression in the @@ -2512,7 +2263,7 @@ private SqlNode registerFrom( // If alias has a column list, introduce a namespace to translate // column names. We skipped registering it just now. if (needAliasNamespace) { - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias, new AliasNamespace(this, call, enclosingNode), @@ -2558,7 +2309,7 @@ private SqlNode registerFrom( final SqlJoin join = (SqlJoin) node; final JoinScope joinScope = new JoinScope(parentScope, usingScope, join); - scopes.put(join, joinScope); + sqlQueryScopes.putScope(join, joinScope); final SqlNode left = join.getLeft(); final SqlNode right = join.getRight(); boolean forceLeftNullable = forceNullable; @@ -2606,11 +2357,11 @@ private SqlNode registerFrom( if (newRight != right) { join.setRight(newRight); } - scopes.putIfAbsent(stripAs(join.getRight()), parentScope); - scopes.putIfAbsent(stripAs(join.getLeft()), parentScope); + sqlQueryScopes.putIfAbsent(stripAs(join.getRight()), parentScope); + sqlQueryScopes.putIfAbsent(stripAs(join.getLeft()), parentScope); registerSubQueries(joinScope, join.getCondition()); final JoinNamespace joinNamespace = new JoinNamespace(this, join); - registerNamespace(null, null, joinNamespace, forceNullable); + sqlQueryScopes.registerNamespace(null, null, joinNamespace, forceNullable); return join; case IDENTIFIER: @@ -2619,10 +2370,12 @@ private SqlNode registerFrom( new IdentifierNamespace( this, id, extendList, enclosingNode, parentScope); - registerNamespace(register ? usingScope : null, alias, newNs, + sqlQueryScopes.registerNamespace(register ? usingScope : null, alias, newNs, forceNullable); + @Nullable TableScope tableScope = sqlQueryScopes.getTableScope(); if (tableScope == null) { tableScope = new TableScope(parentScope, node); + sqlQueryScopes.setTableScope(tableScope); } tableScope.addChild(newNs, requireNonNull(alias, "alias"), forceNullable); if (extendList != null && !extendList.isEmpty()) { @@ -2665,14 +2418,14 @@ private SqlNode registerFrom( final SqlOperator op = call1.getOperator(); if (op instanceof SqlWindowTableFunction && call1.operand(0).getKind() == SqlKind.SELECT) { - scopes.put(node, getSelectScope(call1.operand(0))); + sqlQueryScopes.putScope(node, sqlQueryScopes.getSelectScope(call1.operand(0))); return newNode; } } // Put the usingScope which can be a JoinScope // or a SelectScope, in order to see the left items // of the JOIN tree. - scopes.put(node, usingScope); + sqlQueryScopes.putScope(node, usingScope); return newNode; case UNNEST: @@ -2706,7 +2459,7 @@ private SqlNode registerFrom( } call = (SqlCall) node; final OverScope overScope = new OverScope(usingScope, call); - scopes.put(call, overScope); + sqlQueryScopes.putScope(call, overScope); operand = call.operand(0); newOperand = registerFrom( @@ -2724,7 +2477,7 @@ private SqlNode registerFrom( } for (ScopeChild child : overScope.children) { - registerNamespace(register ? usingScope : null, child.name, + sqlQueryScopes.registerNamespace(register ? usingScope : null, child.name, child.namespace, forceNullable); } @@ -2777,7 +2530,7 @@ private SqlNode registerFrom( // Put the usingScope which can be a JoinScope // or a SelectScope, in order to see the left items // of the JOIN tree. - scopes.put(node, usingScope); + sqlQueryScopes.putScope(node, usingScope); return newNode; default: @@ -2878,15 +2631,15 @@ private void registerQuery( final SqlSelect select = (SqlSelect) node; final SelectNamespace selectNs = createSelectNamespace(select, enclosingNode); - registerNamespace(usingScope, alias, selectNs, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, alias, selectNs, forceNullable); final SqlValidatorScope windowParentScope = first(usingScope, parentScope); SelectScope selectScope = new SelectScope(parentScope, windowParentScope, select); - scopes.put(select, selectScope); + sqlQueryScopes.putScope(select, selectScope); // Start by registering the WHERE clause - clauseScopes.put(IdPair.of(select, Clause.WHERE), selectScope); + sqlQueryScopes.putWhereScope(select, selectScope); registerOperandSubQueries( selectScope, select, @@ -2926,13 +2679,12 @@ private void registerQuery( isAggregate(select) ? new AggregatingSelectScope(selectScope, select, false) : selectScope; - clauseScopes.put(IdPair.of(select, Clause.SELECT), selectScope2); - clauseScopes.put(IdPair.of(select, Clause.MEASURE), - new MeasureScope(selectScope, select)); + sqlQueryScopes.putSelectScope(select, selectScope2); + sqlQueryScopes.putMeasureScope(select, new MeasureScope(selectScope, select)); if (select.getGroup() != null) { GroupByScope groupByScope = new GroupByScope(selectScope, select.getGroup(), select); - clauseScopes.put(IdPair.of(select, Clause.GROUP_BY), groupByScope); + sqlQueryScopes.putGroupByScope(select, groupByScope); registerSubQueries(groupByScope, select.getGroup()); } registerOperandSubQueries( @@ -2951,7 +2703,7 @@ private void registerQuery( : selectScope2; OrderByScope orderScope = new OrderByScope(selectScope3, orderList, select); - clauseScopes.put(IdPair.of(select, Clause.ORDER), orderScope); + sqlQueryScopes.putOrderScope(select, orderScope); registerSubQueries(orderScope, orderList); if (!isAggregate(select)) { @@ -3001,10 +2753,10 @@ private void registerQuery( call = (SqlCall) node; SqlLambdaScope lambdaScope = new SqlLambdaScope(parentScope, (SqlLambda) call); - scopes.put(call, lambdaScope); + sqlQueryScopes.putScope(call, lambdaScope); final LambdaNamespace lambdaNamespace = new LambdaNamespace(this, (SqlLambda) call, node); - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias, lambdaNamespace, @@ -3022,14 +2774,14 @@ private void registerQuery( case VALUES: call = (SqlCall) node; - scopes.put(call, parentScope); + sqlQueryScopes.putScope(call, parentScope); final TableConstructorNamespace tableConstructorNamespace = new TableConstructorNamespace( this, call, parentScope, enclosingNode); - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias, tableConstructorNamespace, @@ -3053,7 +2805,7 @@ private void registerQuery( insertCall, enclosingNode, parentScope); - registerNamespace(usingScope, null, insertNs, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, null, insertNs, forceNullable); registerQuery( parentScope, usingScope, @@ -3071,7 +2823,7 @@ private void registerQuery( deleteCall, enclosingNode, parentScope); - registerNamespace(usingScope, null, deleteNs, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, null, deleteNs, forceNullable); registerQuery( parentScope, usingScope, @@ -3093,7 +2845,7 @@ private void registerQuery( updateCall, enclosingNode, parentScope); - registerNamespace(usingScope, null, updateNs, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, null, updateNs, forceNullable); registerQuery( parentScope, usingScope, @@ -3112,7 +2864,7 @@ private void registerQuery( mergeCall, enclosingNode, parentScope); - registerNamespace(usingScope, null, mergeNs, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, null, mergeNs, forceNullable); registerQuery( parentScope, usingScope, @@ -3128,7 +2880,7 @@ private void registerQuery( SqlUpdate mergeUpdateCall = mergeCall.getUpdateCall(); if (mergeUpdateCall != null) { registerQuery( - getScope(SqlNonNullableAccessors.getSourceSelect(mergeCall), Clause.WHERE), + sqlQueryScopes.getWhereScope(SqlNonNullableAccessors.getSourceSelect(mergeCall)), null, mergeUpdateCall, enclosingNode, @@ -3152,13 +2904,13 @@ private void registerQuery( call = (SqlCall) node; final UnnestNamespace unnestNs = new UnnestNamespace(this, call, parentScope, enclosingNode); - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias, unnestNs, forceNullable); registerOperandSubQueries(parentScope, call, 0); - scopes.put(node, parentScope); + sqlQueryScopes.putScope(node, parentScope); break; case OTHER_FUNCTION: call = (SqlCall) node; @@ -3168,7 +2920,7 @@ private void registerQuery( parentScope, call, enclosingNode); - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias, procNs, @@ -3184,7 +2936,7 @@ private void registerQuery( final CollectNamespace tableConstructorNs = new CollectNamespace(call, cs, enclosingNode); final String alias2 = SqlValidatorUtil.alias(node, nextGeneratedId++); - registerNamespace( + sqlQueryScopes.registerNamespace( usingScope, alias2, tableConstructorNs, @@ -3210,10 +2962,10 @@ private void registerSetop( SqlCall call = (SqlCall) node; final SetopNamespace setopNamespace = createSetopNamespace(call, enclosingNode); - registerNamespace(usingScope, alias, setopNamespace, forceNullable); + sqlQueryScopes.registerNamespace(usingScope, alias, setopNamespace, forceNullable); // A setop is in the same scope as its parent. - scopes.put(call, parentScope); + sqlQueryScopes.putScope(call, parentScope); @NonNull SqlValidatorScope recursiveScope = parentScope; if (enclosingNode.getKind() == SqlKind.WITH_ITEM) { if (node.getKind() != SqlKind.UNION) { @@ -3221,7 +2973,7 @@ private void registerSetop( } else if (call.getOperandList().size() > 2) { throw newValidationError(node, RESOURCE.recursiveWithMustHaveTwoChildUnionSetOp()); } - final WithScope scope = (WithScope) scopes.get(enclosingNode); + final WithScope scope = (WithScope) sqlQueryScopes.getScope(enclosingNode); // recursive scope is only set for the recursive queries. recursiveScope = scope != null && scope.recursiveScope != null ? requireNonNull(scope.recursiveScope) : parentScope; @@ -3249,8 +3001,8 @@ private void registerWith( boolean checkUpdate) { final WithNamespace withNamespace = new WithNamespace(this, with, enclosingNode); - registerNamespace(usingScope, alias, withNamespace, forceNullable); - scopes.put(with, parentScope); + sqlQueryScopes.registerNamespace(usingScope, alias, withNamespace, forceNullable); + sqlQueryScopes.putScope(with, parentScope); SqlValidatorScope scope = parentScope; for (SqlNode withItem_ : with.withList) { @@ -3260,12 +3012,12 @@ private void registerWith( final SqlValidatorScope withScope = new WithScope(scope, withItem, isRecursiveWith ? new WithRecursiveScope(scope, withItem) : null); - scopes.put(withItem, withScope); + sqlQueryScopes.putScope(withItem, withScope); registerQuery(scope, null, withItem.query, withItem.recursive.booleanValue() ? withItem : with, withItem.name.getSimple(), forceNullable); - registerNamespace(null, alias, + sqlQueryScopes.registerNamespace(null, alias, new WithItemNamespace(this, withItem, enclosingNode), false); scope = withScope; @@ -3323,7 +3075,7 @@ protected boolean isOverAggregateWindow(SqlNode node) { /** If there is at least one call to an aggregate function, returns the * first. */ private @Nullable SqlNode getAgg(SqlSelect select) { - final SelectScope selectScope = getRawSelectScope(select); + final SelectScope selectScope = sqlQueryScopes.getRawSelectScope(select); if (selectScope != null) { final List selectList = selectScope.getExpandedSelectList(); if (selectList != null) { @@ -3610,7 +3362,7 @@ protected void validateFrom( // Validate the namespace representation of the node, just in case the // validation did not occur implicitly. - getNamespaceOrThrow(node, scope).validate(targetRowType); + sqlQueryScopes.getNamespaceOrThrow(node, scope).validate(targetRowType); } protected void validateTableFunction(SqlCall node, SqlValidatorScope scope, @@ -3687,7 +3439,7 @@ protected void validateUnnest(SqlCall call, SqlValidatorScope scope, private void checkRollUpInUsing(SqlIdentifier identifier, SqlNode leftOrRight, SqlValidatorScope scope) { - SqlValidatorNamespace namespace = getNamespace(leftOrRight, scope); + SqlValidatorNamespace namespace = sqlQueryScopes.getNamespace(leftOrRight, scope); if (namespace != null) { SqlValidatorTable sqlValidatorTable = namespace.getTable(); if (sqlValidatorTable != null) { @@ -3708,7 +3460,7 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) { final boolean natural = join.isNatural(); final JoinType joinType = join.getJoinType(); final JoinConditionType conditionType = join.getConditionType(); - final SqlValidatorScope joinScope = getScopeOrThrow(join); // getJoinScope? + final SqlValidatorScope joinScope = sqlQueryScopes.getScopeOrThrow(join); // getJoinScope? validateFrom(left, unknownType, joinScope); validateFrom(right, unknownType, joinScope); @@ -3974,7 +3726,7 @@ private RelDataType checkAndDeriveDataType(SqlIdentifier id, SqlNode node) { checkArgument(id.names.size() == 1); String name = id.names.get(0); SqlNameMatcher nameMatcher = getCatalogReader().nameMatcher(); - RelDataType rowType = getNamespaceOrThrow(node).getRowType(); + RelDataType rowType = sqlQueryScopes.getNamespaceOrThrow(node).getRowType(); final RelDataTypeField field = requireNonNull(nameMatcher.field(rowType, name), () -> "unable to find left field " + name + " in " + rowType); @@ -3987,7 +3739,7 @@ private RelDataType validateCommonInputJoinColumn(SqlIdentifier id, SqlNode leftOrRight, SqlValidatorScope scope, boolean natural) { checkArgument(id.names.size() == 1); final String name = id.names.get(0); - final SqlValidatorNamespace namespace = getNamespaceOrThrow(leftOrRight); + final SqlValidatorNamespace namespace = sqlQueryScopes.getNamespaceOrThrow(leftOrRight); final RelDataType rowType = namespace.getRowType(); final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); final RelDataTypeField field = nameMatcher.field(rowType, name); @@ -4024,7 +3776,7 @@ protected void validateSelect( // Namespace is either a select namespace or a wrapper around one. final SelectNamespace ns = - getNamespaceOrThrow(select).unwrap(SelectNamespace.class); + sqlQueryScopes.getNamespaceOrThrow(select).unwrap(SelectNamespace.class); // Its rowtype is null, meaning it hasn't been validated yet. // This is important, because we need to take the targetRowType into @@ -4056,7 +3808,7 @@ protected void validateSelect( } // Make sure that items in FROM clause have distinct aliases. - final SelectScope fromScope = (SelectScope) getFromScope(select); + final SelectScope fromScope = (SelectScope) sqlQueryScopes.getFromScope(select); List<@Nullable String> names = fromScope.getChildNames(); if (!catalogReader.nameMatcher().isCaseSensitive()) { //noinspection RedundantTypeArguments @@ -4115,11 +3867,11 @@ protected void validateSelect( } if (!qualifieds.isEmpty()) { if (select.getWhere() != null) { - forEachQualified(select.getWhere(), getWhereScope(select), + forEachQualified(select.getWhere(), sqlQueryScopes.getWhereScope(select), qualifieds::remove); } if (select.getHaving() != null) { - forEachQualified(select.getHaving(), getHavingScope(select), + forEachQualified(select.getHaving(), sqlQueryScopes.getHavingScope(select), qualifieds::remove); } @@ -4164,8 +3916,8 @@ protected void validateSelect( if (shouldCheckForRollUp(from)) { checkRollUpInSelectList(select); - checkRollUp(null, select, select.getWhere(), getWhereScope(select)); - checkRollUp(null, select, select.getHaving(), getHavingScope(select)); + checkRollUp(null, select, select.getWhere(), sqlQueryScopes.getWhereScope(select)); + checkRollUp(null, select, select.getHaving(), sqlQueryScopes.getHavingScope(select)); checkRollUpInWindowDecl(select); checkRollUpInGroupBy(select); checkRollUpInOrderBy(select); @@ -4186,7 +3938,7 @@ private static void forEachQualified(SqlNode node, SqlValidatorScope scope, } private void checkRollUpInSelectList(SqlSelect select) { - SqlValidatorScope scope = getSelectScope(select); + SqlValidatorScope scope = sqlQueryScopes.getSelectScope(select); for (SqlNode item : SqlNonNullableAccessors.getSelectList(select)) { if (SqlValidatorUtil.isMeasure(item)) { continue; @@ -4199,7 +3951,8 @@ private void checkRollUpInGroupBy(SqlSelect select) { SqlNodeList group = select.getGroup(); if (group != null) { for (SqlNode node : group) { - checkRollUp(null, select, node, getGroupScope(select), "GROUP BY"); + SqlValidatorScope groupScope = sqlQueryScopes.getGroupScope(select); + checkRollUp(null, select, node, groupScope, "GROUP BY"); } } } @@ -4208,7 +3961,8 @@ private void checkRollUpInOrderBy(SqlSelect select) { SqlNodeList orderList = select.getOrderList(); if (orderList != null) { for (SqlNode node : orderList) { - checkRollUp(null, select, node, getOrderScope(select), "ORDER BY"); + SqlValidatorScope orderScope = sqlQueryScopes.getOrderScope(select); + checkRollUp(null, select, node, orderScope, "ORDER BY"); } } } @@ -4227,7 +3981,7 @@ private void checkRollUpInWindow(@Nullable SqlWindow window, SqlValidatorScope s private void checkRollUpInWindowDecl(SqlSelect select) { for (SqlNode decl : select.getWindowList()) { - checkRollUpInWindow((SqlWindow) decl, getSelectScope(select)); + checkRollUpInWindow((SqlWindow) decl, sqlQueryScopes.getSelectScope(select)); } } @@ -4436,7 +4190,7 @@ private static SqlModality deduceModality(SqlNode query) { @Override public boolean validateModality(SqlSelect select, SqlModality modality, boolean fail) { - final SelectScope scope = getRawSelectScopeNonNull(select); + final SelectScope scope = sqlQueryScopes.getRawSelectScopeNonNull(select); switch (modality) { case STREAM: @@ -4561,7 +4315,7 @@ protected void validateWindowClause(SqlSelect select) { return; } - final SelectScope windowScope = (SelectScope) getFromScope(select); + final SelectScope windowScope = (SelectScope) sqlQueryScopes.getFromScope(select); // 1. ensure window names are simple // 2. ensure they are unique within this scope @@ -4614,7 +4368,7 @@ protected void validateQualifyClause(SqlSelect select) { return; } - SqlValidatorScope qualifyScope = getSelectScope(select); + SqlValidatorScope qualifyScope = sqlQueryScopes.getSelectScope(select); qualifyNode = extendedExpand(qualifyNode, qualifyScope, select, Clause.QUALIFY); select.setQualify(qualifyNode); @@ -4639,7 +4393,7 @@ protected void validateQualifyClause(SqlSelect select) { } @Override public void validateWith(SqlWith with, SqlValidatorScope scope) { - final SqlValidatorNamespace namespace = getNamespaceOrThrow(with); + final SqlValidatorNamespace namespace = sqlQueryScopes.getNamespaceOrThrow(with); validateNamespace(namespace, unknownType); } @@ -4722,7 +4476,7 @@ protected void validateOrderList(SqlSelect select) { throw newValidationError(select, RESOURCE.invalidOrderByPos()); } } - final SqlValidatorScope orderScope = getOrderScope(select); + final SqlValidatorScope orderScope = sqlQueryScopes.getOrderScope(select); requireNonNull(orderScope, "orderScope"); List expandList = new ArrayList<>(); @@ -4747,7 +4501,7 @@ protected void validateOrderList(SqlSelect select) { * @param groupByItem GROUP BY clause item */ private void validateGroupByItem(SqlSelect select, SqlNode groupByItem) { - final SqlValidatorScope groupByScope = getGroupScope(select); + final SqlValidatorScope groupByScope = sqlQueryScopes.getGroupScope(select); validateGroupByExpr(groupByItem, groupByScope); groupByScope.validateExpr(groupByItem); } @@ -4792,7 +4546,7 @@ private void validateOrderItem(SqlSelect select, SqlNode orderItem) { break; } - final SqlValidatorScope orderScope = getOrderScope(select); + final SqlValidatorScope orderScope = sqlQueryScopes.getOrderScope(select); validateExpr(orderItem, orderScope); } @@ -4803,7 +4557,7 @@ private void validateOrderItem(SqlSelect select, SqlNode orderItem) { return orderExpr2; } - final SqlValidatorScope scope = getOrderScope(select); + final SqlValidatorScope scope = sqlQueryScopes.getOrderScope(select); inferUnknownTypes(unknownType, scope, orderExpr2); final RelDataType type = deriveType(scope, orderExpr2); setValidatedNodeType(orderExpr2, type); @@ -4835,7 +4589,7 @@ protected void validateGroupClause(SqlSelect select) { } final String clause = "GROUP BY"; validateNoAggs(aggOrOverFinder, groupList, clause); - final SqlValidatorScope groupScope = getGroupScope(select); + final SqlValidatorScope groupScope = sqlQueryScopes.getGroupScope(select); // expand the expression in group list. List expandedList = new ArrayList<>(); @@ -4870,7 +4624,7 @@ protected void validateGroupClause(SqlSelect select) { // Derive the type of each GROUP BY item. We don't need the type, but // it resolves functions, and that is necessary for deducing // monotonicity. - final SqlValidatorScope selectScope = getSelectScope(select); + final SqlValidatorScope selectScope = sqlQueryScopes.getSelectScope(select); AggregatingSelectScope aggregatingScope = null; if (selectScope instanceof AggregatingSelectScope) { aggregatingScope = (AggregatingSelectScope) selectScope; @@ -4925,7 +4679,7 @@ protected void validateWhereClause(SqlSelect select) { if (where == null) { return; } - final SqlValidatorScope whereScope = getWhereScope(select); + final SqlValidatorScope whereScope = sqlQueryScopes.getWhereScope(select); final SqlNode expandedWhere = expand(where, whereScope); select.setWhere(expandedWhere); validateWhereOrOn(whereScope, expandedWhere, "WHERE"); @@ -4970,7 +4724,7 @@ protected void validateHavingClause(SqlSelect select) { } SqlNode originalHaving = having; final AggregatingScope havingScope = - (AggregatingScope) getSelectScope(select); + (AggregatingScope) sqlQueryScopes.getSelectScope(select); if (config.conformance().isHavingAlias()) { SqlNode newExpr = extendedExpand(having, havingScope, select, Clause.HAVING); if (having != newExpr) { @@ -4996,7 +4750,7 @@ protected RelDataType validateSelectList(final SqlNodeList selectItems, // are ignored. // Validate SELECT list. Expand terms of the form "*" or "TABLE.*". - final SqlValidatorScope selectScope = getSelectScope(select); + final SqlValidatorScope selectScope = sqlQueryScopes.getSelectScope(select); final List expandedSelectItems = new ArrayList<>(); final Set aliases = new HashSet<>(); final PairList fieldList = PairList.of(); @@ -5027,7 +4781,7 @@ protected RelDataType validateSelectList(final SqlNodeList selectItems, if (config.identifierExpansion()) { select.setSelectList(newSelectList); } - getRawSelectScopeNonNull(select).setExpandedSelectList(expandedSelectItems); + sqlQueryScopes.getRawSelectScopeNonNull(select).setExpandedSelectList(expandedSelectItems); // TODO: when SELECT appears as a value sub-query, should be using // something other than unknownType for targetRowType @@ -5075,7 +4829,7 @@ private void validateExpr(SqlNode expr, SqlValidatorScope scope) { } if (SqlValidatorUtil.isMeasure(expr) && scope instanceof SelectScope) { - scope = getMeasureScope(((SelectScope) scope).getNode()); + scope = sqlQueryScopes.getMeasureScope(((SelectScope) scope).getNode()); } // Call on the expression to validate itself. @@ -5114,7 +4868,7 @@ private void handleScalarSubQuery(SqlSelect parentSelect, SqlValidatorUtil.alias(selectItem, aliasList.size()); aliasList.add(alias); - final SelectScope scope = (SelectScope) getWhereScope(parentSelect); + final SelectScope scope = (SelectScope) sqlQueryScopes.getWhereScope(parentSelect); final RelDataType type = deriveType(scope, selectItem); setValidatedNodeType(selectItem, type); @@ -5176,7 +4930,7 @@ protected RelDataType createTargetRowType( } @Override public void validateInsert(SqlInsert insert) { - final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert); + final SqlValidatorNamespace targetNamespace = sqlQueryScopes.getNamespaceOrThrow(insert); validateNamespace(targetNamespace, unknownType); final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable(targetNamespace, @@ -5199,7 +4953,7 @@ protected RelDataType createTargetRowType( final SqlSelect sqlSelect = (SqlSelect) source; validateSelect(sqlSelect, targetRowType); } else { - final SqlValidatorScope scope = scopes.get(source); + final SqlValidatorScope scope = sqlQueryScopes.getScope(source); requireNonNull(scope, "scope"); validateQuery(source, scope, targetRowType); } @@ -5209,7 +4963,7 @@ protected RelDataType createTargetRowType( // from validateSelect above). It would be better if that information // were used here so that we never saw any untyped nulls during // checkTypeAssignment. - final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType(); + final RelDataType sourceRowType = sqlQueryScopes.getNamespaceOrThrow(source).getRowType(); final RelDataType logicalTargetRowType = getLogicalTargetRowType(targetRowType, insert); setValidatedNodeType(insert, logicalTargetRowType); @@ -5235,7 +4989,7 @@ protected RelDataType createTargetRowType( targetRowTypeToValidate, realTargetRowType, source, logicalSourceRowType, logicalTargetRowType); - checkTypeAssignment(scopes.get(source), + checkTypeAssignment(sqlQueryScopes.getScope(source), table, logicalSourceRowType, targetRowTypeToValidate, @@ -5444,14 +5198,14 @@ protected RelDataType getLogicalTargetRowType( && this.config.conformance().isInsertSubsetColumnsAllowed()) { // Target an implicit subset of columns. final SqlNode source = insert.getSource(); - final RelDataType sourceRowType = getNamespaceOrThrow(source).getRowType(); + final RelDataType sourceRowType = sqlQueryScopes.getNamespaceOrThrow(source).getRowType(); final RelDataType logicalSourceRowType = getLogicalSourceRowType(sourceRowType, insert); final RelDataType implicitTargetRowType = typeFactory.createStructType( targetRowType.getFieldList() .subList(0, logicalSourceRowType.getFieldCount())); - final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(insert); + final SqlValidatorNamespace targetNamespace = sqlQueryScopes.getNamespaceOrThrow(insert); validateNamespace(targetNamespace, implicitTargetRowType); return implicitTargetRowType; } else { @@ -5594,7 +5348,7 @@ private static SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { final SqlSelect sqlSelect = SqlNonNullableAccessors.getSourceSelect(call); validateSelect(sqlSelect, unknownType); - final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call); + final SqlValidatorNamespace targetNamespace = sqlQueryScopes.getNamespaceOrThrow(call); validateNamespace(targetNamespace, unknownType); final SqlValidatorTable table = targetNamespace.getTable(); @@ -5602,7 +5356,7 @@ private static SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { } @Override public void validateUpdate(SqlUpdate call) { - final SqlValidatorNamespace targetNamespace = getNamespaceOrThrow(call); + final SqlValidatorNamespace targetNamespace = sqlQueryScopes.getNamespaceOrThrow(call); validateNamespace(targetNamespace, unknownType); final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable(targetNamespace, @@ -5619,7 +5373,7 @@ private static SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { validateSelect(select, targetRowType); final RelDataType sourceRowType = getValidatedNodeType(select); - checkTypeAssignment(scopes.get(select), table, sourceRowType, targetRowType, + checkTypeAssignment(sqlQueryScopes.getScope(select), table, sourceRowType, targetRowType, call); checkConstraint(table, call, targetRowType); @@ -5642,7 +5396,7 @@ private static SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) { // since validateSelect() would bail. // Let's use the update/insert targetRowType when available. IdentifierNamespace targetNamespace = - (IdentifierNamespace) getNamespaceOrThrow(call.getTargetTable()); + (IdentifierNamespace) sqlQueryScopes.getNamespaceOrThrow(call.getTargetTable()); validateNamespace(targetNamespace, unknownType); SqlValidatorTable table = targetNamespace.getTable(); @@ -5982,10 +5736,10 @@ public void setOriginal(SqlNode expr, SqlNode original) { } @Override public void validateLambda(SqlLambda lambdaExpr) { - final SqlLambdaScope scope = (SqlLambdaScope) scopes.get(lambdaExpr); + final SqlLambdaScope scope = (SqlLambdaScope) sqlQueryScopes.getScope(lambdaExpr); requireNonNull(scope, "scope"); final LambdaNamespace ns = - getNamespaceOrThrow(lambdaExpr).unwrap(LambdaNamespace.class); + sqlQueryScopes.getNamespaceOrThrow(lambdaExpr).unwrap(LambdaNamespace.class); deriveType(scope, lambdaExpr.getExpression()); RelDataType type = deriveTypeImpl(scope, lambdaExpr); @@ -5996,10 +5750,10 @@ public void setOriginal(SqlNode expr, SqlNode original) { @Override public void validateMatchRecognize(SqlCall call) { final SqlMatchRecognize matchRecognize = (SqlMatchRecognize) call; final MatchRecognizeScope scope = - (MatchRecognizeScope) getMatchRecognizeScope(matchRecognize); + (MatchRecognizeScope) sqlQueryScopes.getMatchRecognizeScope(matchRecognize); final MatchRecognizeNamespace ns = - getNamespaceOrThrow(call).unwrap(MatchRecognizeNamespace.class); + sqlQueryScopes.getNamespaceOrThrow(call).unwrap(MatchRecognizeNamespace.class); assert ns.rowType == null; // rows per match @@ -6043,7 +5797,7 @@ public void setOriginal(SqlNode expr, SqlNode original) { if (allRows) { final SqlValidatorNamespace sqlNs = - getNamespaceOrThrow(matchRecognize.getTableRef()); + sqlQueryScopes.getNamespaceOrThrow(matchRecognize.getTableRef()); final RelDataType inputDataType = sqlNs.getRowType(); for (RelDataTypeField fs : inputDataType.getFieldList()) { if (!typeBuilder.nameExists(fs.getName())) { @@ -6135,7 +5889,7 @@ public void setOriginal(SqlNode expr, SqlNode original) { final RelDataType rowType; if (matchRecognize.getMeasureList().isEmpty()) { - rowType = getNamespaceOrThrow(matchRecognize.getTableRef()).getRowType(); + rowType = sqlQueryScopes.getNamespaceOrThrow(matchRecognize.getTableRef()).getRowType(); } else { rowType = typeBuilder.build(); } @@ -6251,10 +6005,10 @@ private static String alias(SqlNode item) { } public void validatePivot(SqlPivot pivot) { - final PivotScope scope = (PivotScope) getJoinScope(pivot); + final PivotScope scope = (PivotScope) sqlQueryScopes.getJoinScope(pivot); final PivotNamespace ns = - getNamespaceOrThrow(pivot).unwrap(PivotNamespace.class); + sqlQueryScopes.getNamespaceOrThrow(pivot).unwrap(PivotNamespace.class); assert ns.rowType == null; // Given @@ -6327,10 +6081,10 @@ public void validatePivot(SqlPivot pivot) { } public void validateUnpivot(SqlUnpivot unpivot) { - final UnpivotScope scope = (UnpivotScope) getJoinScope(unpivot); + final UnpivotScope scope = (UnpivotScope) sqlQueryScopes.getJoinScope(unpivot); final UnpivotNamespace ns = - getNamespaceOrThrow(unpivot).unwrap(UnpivotNamespace.class); + sqlQueryScopes.getNamespaceOrThrow(unpivot).unwrap(UnpivotNamespace.class); assert ns.rowType == null; // Given @@ -6370,7 +6124,7 @@ public void validateUnpivot(SqlUnpivot unpivot) { // What columns from the input are not referenced by a column in the IN // list? final SqlValidatorNamespace inputNs = - requireNonNull(getNamespace(unpivot.query)); + requireNonNull(sqlQueryScopes.getNamespace(unpivot.query)); final Set unusedColumnNames = catalogReader.nameMatcher().createSet(); unusedColumnNames.addAll(inputNs.getRowType().getFieldNames()); @@ -6705,7 +6459,7 @@ public SqlNode extendedExpandGroupBy(SqlNode expr, private @Nullable List getFieldOrigin(SqlNode sqlQuery, int i) { if (sqlQuery instanceof SqlSelect) { SqlSelect sqlSelect = (SqlSelect) sqlQuery; - final SelectScope scope = getRawSelectScopeNonNull(sqlSelect); + final SelectScope scope = sqlQueryScopes.getRawSelectScopeNonNull(sqlSelect); final List selectList = requireNonNull(scope.getExpandedSelectList(), () -> "expandedSelectList for " + scope); @@ -6720,7 +6474,7 @@ public SqlNode extendedExpandGroupBy(SqlNode expr, AliasNamespace aliasNs = namespace.unwrap(AliasNamespace.class); SqlNode aliased = requireNonNull(aliasNs.getNode(), () -> "sqlNode for aliasNs " + aliasNs); - namespace = getNamespaceOrThrow(stripAs(aliased)); + namespace = sqlQueryScopes.getNamespaceOrThrow(stripAs(aliased)); } final SqlValidatorTable table = namespace.getTable(); @@ -7146,10 +6900,10 @@ class OrderExpressionExpander extends SqlScopedShuttle { private final SqlNode root; OrderExpressionExpander(SqlSelect select, SqlNode root) { - super(getOrderScope(select)); + super(sqlQueryScopes.getOrderScope(select)); this.select = select; this.root = root; - this.aliasList = getNamespaceOrThrow(select).getRowType().getFieldNames(); + this.aliasList = sqlQueryScopes.getNamespaceOrThrow(select).getRowType().getFieldNames(); } public SqlNode go() { @@ -7214,7 +6968,7 @@ private SqlNode nthSelectItem(int ordinal, final SqlParserPos pos) { if (id.isSimple() && config.conformance().isSortByAlias()) { String alias = id.getSimple(); - final SqlValidatorNamespace selectNs = getNamespaceOrThrow(select); + final SqlValidatorNamespace selectNs = sqlQueryScopes.getNamespaceOrThrow(select); final RelDataType rowType = selectNs.getRowTypeSansSystemColumns(); final SqlNameMatcher nameMatcher = catalogReader.nameMatcher(); @@ -7289,7 +7043,7 @@ static class ExtendedExpander extends Expander { final boolean replaceAliases = clause.shouldReplaceAliases(validator.config); if (!replaceAliases) { - final SelectScope scope = validator.getRawSelectScopeNonNull(select); + final SelectScope scope = validator.sqlQueryScopes.getRawSelectScopeNonNull(select); SqlNode node = expandCommonColumn(select, id, scope, validator); if (node != id) { return node; @@ -7877,52 +7631,5 @@ public enum Status { VALID } - /** Allows {@link #clauseScopes} to have multiple values per SELECT. */ - private enum Clause { - WHERE, - GROUP_BY, - SELECT, - MEASURE, - ORDER, - CURSOR, - HAVING, - QUALIFY; - /** - * Determines if the extender should replace aliases with expanded values. - * For example: - * - *
{@code
-     * SELECT a + a as twoA
-     * GROUP BY twoA
-     * }
- * - *

turns into - * - *

{@code
-     * SELECT a + a as twoA
-     * GROUP BY a + a
-     * }
- * - *

This is determined both by the clause and the config. - * - * @param config The configuration - * @return Whether we should replace the alias with its expanded value - */ - boolean shouldReplaceAliases(Config config) { - switch (this) { - case GROUP_BY: - return config.conformance().isGroupByAlias(); - - case HAVING: - return config.conformance().isHavingAlias(); - - case QUALIFY: - return true; - - default: - throw Util.unexpected(this); - } - } - } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java index 77a8dc070e2c..dee7defb593a 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorNamespace.java @@ -42,7 +42,7 @@ * {@link IdentifierNamespace} for table names, {@link SelectNamespace} for * SELECT queries, {@link SetopNamespace} for UNION, EXCEPT and INTERSECT, and * so forth. But if you are looking at a SELECT query and call - * {@link SqlValidator#getNamespace(org.apache.calcite.sql.SqlNode)}, you may + * {@link SqlQueryScopes#getNamespace(org.apache.calcite.sql.SqlNode)}, you may * not get a SelectNamespace. Why? Because the validator is allowed to wrap * namespaces in other objects which implement * {@link SqlValidatorNamespace}. Your SelectNamespace will be there somewhere, diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java index 4d59c96d1507..70e8c61af067 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorScope.java @@ -317,7 +317,8 @@ class ResolvedImpl implements Resolved { boolean nullable, SqlValidatorScope scope, Path path, List remainingNames) { if (scope instanceof TableScope) { - scope = scope.getValidator().getSelectScope((SqlSelect) scope.getNode()); + scope = scope.getValidator().getSqlQueryScopes() + .getSelectScope((SqlSelect) scope.getNode()); } if (scope instanceof AggregatingSelectScope) { scope = ((AggregatingSelectScope) scope).parent; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java index 2bba7a22bb14..5af293e8c985 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/TableScope.java @@ -54,7 +54,7 @@ class TableScope extends ListScope { if (this == scope2) { return true; } - SqlValidatorScope s = getValidator().getSelectScope((SqlSelect) node); + SqlValidatorScope s = getValidator().getSqlQueryScopes().getSelectScope((SqlSelect) node); return s.isWithin(scope2); } } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/UnpivotScope.java b/core/src/main/java/org/apache/calcite/sql/validate/UnpivotScope.java index f44f14b39de3..5667de8aa19b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/UnpivotScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/UnpivotScope.java @@ -39,7 +39,7 @@ public UnpivotScope(SqlValidatorScope parent, SqlUnpivot unpivot) { * scope only has one namespace, and it is anonymous. */ public SqlValidatorNamespace getChild() { return requireNonNull( - validator.getNamespace(unpivot.query), + validator.getSqlQueryScopes().getNamespace(unpivot.query), () -> "namespace for unpivot.query " + unpivot.query); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java index 5c0a85756fab..2353b413e609 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithItemNamespace.java @@ -39,7 +39,7 @@ class WithItemNamespace extends AbstractNamespace { @Override protected RelDataType validateImpl(RelDataType targetRowType) { final SqlValidatorNamespace childNs = - validator.getNamespaceOrThrow(getQuery()); + validator.getSqlQueryScopes().getNamespaceOrThrow(getQuery()); final RelDataType rowType = childNs.getRowTypeSansSystemColumns(); mustFilterFields = childNs.getMustFilterFields(); SqlNodeList columnList = withItem.columnList; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java index bec57b715b1d..f0999e73df21 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithNamespace.java @@ -57,9 +57,9 @@ public class WithNamespace extends AbstractNamespace { validator.validateWithItem((SqlWithItem) withItem); } final SqlValidatorScope scope2 = - validator.getWithScope(Util.last(with.withList)); + validator.getSqlQueryScopes().getWithScope(Util.last(with.withList)); final SqlValidatorNamespace bodyNamespace = - requireNonNull(validator.getNamespace(with.body), "namespace"); + requireNonNull(validator.getSqlQueryScopes().getNamespace(with.body), "namespace"); validator.validateQuery(with.body, scope2, targetRowType); final RelDataType rowType = validator.getValidatedNodeType(with.body); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithRecursiveScope.java b/core/src/main/java/org/apache/calcite/sql/validate/WithRecursiveScope.java index 1fb33e643446..e13b7f35188b 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithRecursiveScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithRecursiveScope.java @@ -53,7 +53,7 @@ class WithRecursiveScope extends ListScope { @Override public @Nullable SqlValidatorNamespace getTableNamespace(List names) { if (names.size() == 1 && names.get(0).equals(withItem.name.getSimple())) { - return validator.getNamespace(withItem); + return validator.getSqlQueryScopes().getNamespace(withItem); } return super.getTableNamespace(names); } @@ -62,7 +62,7 @@ class WithRecursiveScope extends ListScope { SqlNameMatcher nameMatcher, Path path, Resolved resolved) { if (names.size() == 1 && names.equals(withItem.name.names)) { - final SqlValidatorNamespace ns = validator.getNamespaceOrThrow(withItem); + final SqlValidatorNamespace ns = validator.getSqlQueryScopes().getNamespaceOrThrow(withItem); // create a recursive name space so that we can create a WITH_ITEM_TABLE_REFs final SqlValidatorNamespace recursiveNS = new WithItemRecursiveNamespace(this.validator, withItem, ns.getEnclosingNode()); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java index 7d6229895751..cddcc3393770 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/WithScope.java @@ -56,7 +56,7 @@ class WithScope extends ListScope { @Override public @Nullable SqlValidatorNamespace getTableNamespace(List names) { if (names.size() == 1 && names.get(0).equals(withItem.name.getSimple())) { - return validator.getNamespace(withItem); + return validator.getSqlQueryScopes().getNamespace(withItem); } return super.getTableNamespace(names); } @@ -65,7 +65,7 @@ class WithScope extends ListScope { SqlNameMatcher nameMatcher, Path path, Resolved resolved) { if (names.size() == 1 && names.equals(withItem.name.names)) { - final SqlValidatorNamespace ns = validator.getNamespaceOrThrow(withItem); + final SqlValidatorNamespace ns = validator.getSqlQueryScopes().getNamespaceOrThrow(withItem); final Step path2 = path .plus(ns.getRowType(), 0, names.get(0), StructKind.FULLY_QUALIFIED); resolved.found(ns, false, this, path2, ImmutableList.of()); diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java index 847579771711..7a220af551b2 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/AbstractTypeCoercion.java @@ -323,7 +323,7 @@ private static SqlNode castTo(SqlNode node, RelDataType type) { */ protected void updateInferredType(SqlNode node, RelDataType type) { validator.setValidatedNodeType(node, type); - final SqlValidatorNamespace namespace = validator.getNamespace(node); + final SqlValidatorNamespace namespace = validator.getSqlQueryScopes().getNamespace(node); if (namespace != null) { namespace.setType(type); } diff --git a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java index 73de170e5c12..9f798b474c0e 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/implicit/TypeCoercionImpl.java @@ -98,7 +98,7 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) switch (kind) { case SELECT: SqlSelect selectNode = (SqlSelect) query; - SqlValidatorScope scope1 = validator.getSelectScope(selectNode); + SqlValidatorScope scope1 = validator.getSqlQueryScopes().getSelectScope(selectNode); if (!coerceColumnType(scope1, getSelectList(selectNode), columnIndex, targetType)) { return false; } @@ -118,7 +118,8 @@ public TypeCoercionImpl(RelDataTypeFactory typeFactory, SqlValidator validator) return coerceValues; case WITH: SqlNode body = ((SqlWith) query).body; - return rowTypeCoercion(validator.getOverScope(query), body, columnIndex, targetType); + return rowTypeCoercion( + validator.getSqlQueryScopes().getOverScope(query), body, columnIndex, targetType); case UNION: case INTERSECT: case EXCEPT: @@ -563,7 +564,7 @@ protected boolean booleanEquality(SqlCallBinding binding, } else { // Another sub-query. SqlValidatorScope scope1 = node2 instanceof SqlSelect - ? validator.getSelectScope((SqlSelect) node2) + ? validator.getSqlQueryScopes().getSelectScope((SqlSelect) node2) : scope; coerced = rowTypeCoercion(scope1, node2, i, desired) || coerced; } 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 12222b218b09..23a8af7e81c2 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -169,6 +169,7 @@ import org.apache.calcite.sql.validate.SqlMonotonicity; import org.apache.calcite.sql.validate.SqlNameMatcher; import org.apache.calcite.sql.validate.SqlQualified; +import org.apache.calcite.sql.validate.SqlQueryScopes; import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction; import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro; import org.apache.calcite.sql.validate.SqlValidator; @@ -386,6 +387,10 @@ private SqlValidator validator() { return requireNonNull(validator, "validator"); } + private SqlQueryScopes sqlQueryScopes() { + return requireNonNull(validator().getSqlQueryScopes(), "sqlQueryScopes"); + } + private T getNamespace(SqlNode node) { return requireNonNull(getNamespaceOrNull(node), () -> "Namespace is not found for " + node); @@ -393,7 +398,7 @@ private T getNamespace(SqlNode node) { @SuppressWarnings("unchecked") private @Nullable T getNamespaceOrNull(SqlNode node) { - return (@Nullable T) validator().getNamespace(node); + return (@Nullable T) sqlQueryScopes().getNamespace(node); } /** Returns the RelOptCluster in use. */ @@ -727,9 +732,9 @@ private static RelCollation requiredCollation(RelNode r) { * Converts a SELECT statement's parse tree into a relational expression. */ public RelNode convertSelect(SqlSelect select, boolean top) { - final SqlValidatorScope selectScope = validator().getWhereScope(select); + final SqlValidatorScope selectScope = sqlQueryScopes().getWhereScope(select); final MeasureScope measureScope = - (MeasureScope) validator().getMeasureScope(select); + (MeasureScope) sqlQueryScopes().getMeasureScope(select); final Blackboard bb = createBlackboard(selectScope, null, top); final Blackboard measureBb = new MeasureBlackboard(measureScope, bb); convertSelectImpl(bb, measureBb, select); @@ -1362,8 +1367,8 @@ private void substituteSubQuery(Blackboard bb, SubQuery subQuery) { query = call.operand(0); final SqlValidatorScope seekScope = (query instanceof SqlSelect) - ? validator().getSelectScope((SqlSelect) query) - : validator().getEmptyScope(); + ? sqlQueryScopes().getSelectScope((SqlSelect) query) + : validator().createEmptyScope(); final Blackboard seekBb = createBlackboard(seekScope, null, false); final RelNode seekRel = convertQueryOrInList(seekBb, query, null); requireNonNull(seekRel, () -> "seekRel is null for query " + query); @@ -1444,8 +1449,8 @@ private void substituteSubQueryOfSetSemanticsInputTable( query = call.operand(0); final SqlValidatorScope innerTableScope = (query instanceof SqlSelect) - ? validator().getSelectScope((SqlSelect) query) - : validator().getEmptyScope(); + ? sqlQueryScopes().getSelectScope((SqlSelect) query) + : validator().createEmptyScope(); final Blackboard setSemanticsTableBb = createBlackboard(innerTableScope, null, false); final RelNode inputOfSetSemanticsTable = @@ -1846,8 +1851,8 @@ private RelOptUtil.Exists convertExists( @Nullable RelDataType targetDataType) { final SqlValidatorScope seekScope = (seek instanceof SqlSelect) - ? validator().getSelectScope((SqlSelect) seek) - : validator().getEmptyScope(); + ? sqlQueryScopes().getSelectScope((SqlSelect) seek) + : validator().createEmptyScope(); final Blackboard seekBb = createBlackboard(seekScope, null, false); RelNode seekRel = convertQueryOrInList(seekBb, seek, targetDataType); requireNonNull(seekRel, () -> "seekRel is null for query " + seek); @@ -2220,7 +2225,7 @@ public RexNode convertExpression( */ private RexNode convertLambda(Blackboard bb, SqlNode node) { final SqlLambda call = (SqlLambda) node; - final SqlLambdaScope scope = (SqlLambdaScope) validator().getLambdaScope(call); + final SqlLambdaScope scope = (SqlLambdaScope) sqlQueryScopes().getLambdaScope(call); final Map nameToNodeMap = new HashMap<>(); final List parameters = new ArrayList<>(scope.getParameterTypes().size()); @@ -2553,7 +2558,7 @@ private void convertUnnest(Blackboard bb, SqlCall call, @Nullable List f protected void convertMatchRecognize(Blackboard bb, SqlMatchRecognize matchRecognize) { final SqlValidatorNamespace ns = getNamespace(matchRecognize); - final SqlValidatorScope scope = validator().getMatchRecognizeScope(matchRecognize); + final SqlValidatorScope scope = sqlQueryScopes().getMatchRecognizeScope(matchRecognize); final Blackboard matchBb = createBlackboard(scope, null, false); final RelDataType rowType = ns.getRowType(); @@ -2712,7 +2717,7 @@ protected void convertMatchRecognize(Blackboard bb, } protected void convertPivot(Blackboard bb, SqlPivot pivot) { - final SqlValidatorScope scope = validator().getJoinScope(pivot); + final SqlValidatorScope scope = sqlQueryScopes().getJoinScope(pivot); final Blackboard pivotBb = createBlackboard(scope, null, false); // Convert input @@ -2794,7 +2799,7 @@ protected void convertPivot(Blackboard bb, SqlPivot pivot) { } protected void convertUnpivot(Blackboard bb, SqlUnpivot unpivot) { - final SqlValidatorScope scope = validator().getJoinScope(unpivot); + final SqlValidatorScope scope = sqlQueryScopes().getJoinScope(unpivot); final Blackboard unpivotBb = createBlackboard(scope, null, false); // Convert input @@ -3268,17 +3273,16 @@ protected List getSystemFields() { } private void convertJoin(Blackboard bb, SqlJoin join) { - SqlValidator validator = validator(); - final SqlValidatorScope scope = validator.getJoinScope(join); + final SqlValidatorScope scope = sqlQueryScopes().getJoinScope(join); final Blackboard fromBlackboard = createBlackboard(scope, null, false); SqlNode left = join.getLeft(); SqlNode right = join.getRight(); JoinType joinType = join.getJoinType(); - final SqlValidatorScope leftScope = validator.getJoinScope(left); + final SqlValidatorScope leftScope = sqlQueryScopes().getJoinScope(left); final Blackboard leftBlackboard = createBlackboard(leftScope, null, false); - final SqlValidatorScope rightScope = validator.getJoinScope(right); + final SqlValidatorScope rightScope = sqlQueryScopes().getJoinScope(right); final Blackboard rightBlackboard = createBlackboard(rightScope, null, false); convertFrom(leftBlackboard, left); @@ -3492,7 +3496,7 @@ protected void convertAgg(Blackboard bb, SqlSelect select, final AggConverter aggConverter = AggConverter.create(bb, - (AggregatingSelectScope) validator().getSelectScope(select)); + (AggregatingSelectScope) sqlQueryScopes().getSelectScope(select)); createAggImpl(bb, aggConverter, selectList, groupList, having, orderExprList); } @@ -3818,7 +3822,7 @@ protected RelFieldCollation convertOrderItem( // Scan the select list and order exprs for an identical expression. final SelectScope selectScope = - requireNonNull(validator.getRawSelectScope(select), + requireNonNull(sqlQueryScopes().getRawSelectScope(select), () -> "getRawSelectScope is not found for " + select); int ordinal = -1; List expandedSelectList = selectScope.getExpandedSelectList(); @@ -3913,7 +3917,7 @@ protected RelRoot convertQueryRecursive(SqlNode query, boolean top, private RelNode createUnion(SqlCall call, RelNode left, RelNode right) { - SqlValidatorNamespace nameSpace = this.validator().getNamespace(call); + SqlValidatorNamespace nameSpace = sqlQueryScopes().getNamespace(call); boolean all = all(call); if (nameSpace != null) { SqlNode enclosingNode = nameSpace.getEnclosingNode(); @@ -4219,7 +4223,7 @@ private Blackboard createInsertBlackboard(RelOptTable targetTable, rexBuilder.makeFieldAccess(sourceRef, j++)); } } - return createBlackboard(validator().getEmptyScope(), nameToNodeMap, false); + return createBlackboard(validator().createEmptyScope(), nameToNodeMap, false); } private static InitializerExpressionFactory getInitializerFactory( @@ -4341,7 +4345,7 @@ private RelNode convertUpdate(SqlUpdate call) { final SqlSelect sourceSelect = requireNonNull(call.getSourceSelect(), () -> "sourceSelect for " + call); - final SqlValidatorScope scope = validator().getWhereScope(sourceSelect); + final SqlValidatorScope scope = sqlQueryScopes().getWhereScope(sourceSelect); Blackboard bb = createBlackboard(scope, null, false); replaceSubQueries(bb, call, RelOptUtil.Logic.TRUE_FALSE_UNKNOWN); @@ -4936,7 +4940,7 @@ public RelNode convertValues( SqlCall values, @Nullable RelDataType targetRowType) { final SqlValidatorScope scope = - requireNonNull(validator().getOverScope(values)); + requireNonNull(sqlQueryScopes().getOverScope(values)); final Blackboard bb = createBlackboard(scope, null, false); convertValuesImpl(bb, values, targetRowType); return bb.root(); diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java index 41619a9d9200..7edc42b28b02 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -15873,7 +15873,7 @@ void testCastTruncates(CastType castType, SqlOperatorFixture f) { final SqlOperatorFixture f = fixture(); final SqlValidatorImpl validator = (SqlValidatorImpl) f.getFactory().createValidator(); - final SqlValidatorScope scope = validator.getEmptyScope(); + final SqlValidatorScope scope = validator.createEmptyScope(); final RelDataTypeFactory typeFactory = validator.getTypeFactory(); final Builder builder = new Builder(typeFactory); builder.add0(SqlTypeName.BOOLEAN, true, false); diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlValidatorFixture.java b/testkit/src/main/java/org/apache/calcite/test/SqlValidatorFixture.java index c55158729300..20e56962e23a 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlValidatorFixture.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlValidatorFixture.java @@ -273,7 +273,7 @@ public SqlValidatorFixture assertMonotonicity( (sap, validator, n) -> { final RelDataType rowType = validator.getValidatedNodeType(n); final SqlValidatorNamespace selectNamespace = - validator.getNamespace(n); + validator.getSqlQueryScopes().getNamespace(n); final String field0 = rowType.getFieldList().get(0).getName(); final SqlMonotonicity monotonicity = selectNamespace.getMonotonicity(field0);