diff --git a/core/src/main/java/org/apache/calcite/sql/validate/NamespaceBuilder.java b/core/src/main/java/org/apache/calcite/sql/validate/NamespaceBuilder.java new file mode 100644 index 000000000000..d42a669fe278 --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/validate/NamespaceBuilder.java @@ -0,0 +1,195 @@ +/* + * 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.SqlDelete; +import org.apache.calcite.sql.SqlInsert; +import org.apache.calcite.sql.SqlMatchRecognize; +import org.apache.calcite.sql.SqlMerge; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlPivot; +import org.apache.calcite.sql.SqlSelect; +import org.apache.calcite.sql.SqlUnpivot; +import org.apache.calcite.sql.SqlUpdate; + +import org.checkerframework.checker.initialization.qual.UnknownInitialization; +import org.checkerframework.checker.nullness.qual.Nullable; + +import static java.util.Objects.requireNonNull; + +/** + * A builder to allow for customization of namespaces down stream. + */ +public class NamespaceBuilder { + + private final SqlValidatorImpl sqlValidatorImpl; + + public NamespaceBuilder(@UnknownInitialization SqlValidatorImpl sqlValidatorImpl) { + @SuppressWarnings("argument.type.incompatible") + SqlValidatorImpl sqlValidatorCast = sqlValidatorImpl; + this.sqlValidatorImpl = sqlValidatorCast; + } + + /** + * Creates a namespace for a SELECT node. Derived class may + * override this factory method. + * + * @param select Select node + * @param enclosingNode Enclosing node + * @return Select namespace + */ + public SelectNamespace createSelectNamespace( + SqlSelect select, + SqlNode enclosingNode) { + return new SelectNamespace(sqlValidatorImpl, select, enclosingNode); + } + + /** + * Creates a namespace for a set operation (UNION, + * INTERSECT, or EXCEPT). Derived class may override + * this factory method. + * + * @param call Call to set operation + * @param enclosingNode Enclosing node + * @return Set operation namespace + */ + public SetopNamespace createSetopNamespace( + SqlCall call, + SqlNode enclosingNode) { + return new SetopNamespace(sqlValidatorImpl, call, enclosingNode); + } + + + public MatchRecognizeNamespace createMatchRecognizeNameSpace( + SqlMatchRecognize call, + SqlNode enclosingNode) { + return new MatchRecognizeNamespace(sqlValidatorImpl, call, enclosingNode); + } + + public UnpivotNamespace createUnpivotNameSpace( + SqlUnpivot call, + SqlNode enclosingNode) { + return new UnpivotNamespace(sqlValidatorImpl, call, enclosingNode); + } + + + public PivotNamespace createPivotNameSpace( + SqlPivot call, + SqlNode enclosingNode) { + return new PivotNamespace(sqlValidatorImpl, call, enclosingNode); + } + + public DmlNamespace createDeleteNamespace(SqlDelete delete, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + return new DeleteNamespace(sqlValidatorImpl, delete, enclosingNode, parentScope); + } + + public DmlNamespace createInsertNamespace(SqlInsert insert, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + return new InsertNamespace(sqlValidatorImpl, insert, enclosingNode, parentScope); + } + + public DmlNamespace createMergeNamespace(SqlMerge merge, SqlNode enclosing, + SqlValidatorScope parentScope) { + return new MergeNamespace(sqlValidatorImpl, merge, enclosing, parentScope); + } + + public DmlNamespace createUpdate(SqlUpdate sqlUpdate, SqlNode enclosing, + SqlValidatorScope parentScope) { + return new UpdateNamespace(sqlValidatorImpl, sqlUpdate, enclosing, parentScope); + } + + + /** + * Common base class for DML statement namespaces. + */ + public abstract static class DmlNamespace extends IdentifierNamespace { + protected DmlNamespace(SqlValidatorImpl validator, SqlNode id, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + super(validator, id, enclosingNode, parentScope); + } + } + + /** + * Namespace for an INSERT statement. + */ + private static class InsertNamespace extends DmlNamespace { + private final SqlInsert node; + + InsertNamespace(SqlValidatorImpl validator, SqlInsert node, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + super(validator, node.getTargetTable(), enclosingNode, parentScope); + this.node = requireNonNull(node, "node"); + } + + @Override public @Nullable SqlNode getNode() { + return node; + } + } + + /** + * Namespace for an UPDATE statement. + */ + private static class UpdateNamespace extends DmlNamespace { + private final SqlUpdate node; + + UpdateNamespace(SqlValidatorImpl validator, SqlUpdate node, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + super(validator, node.getTargetTable(), enclosingNode, parentScope); + this.node = requireNonNull(node, "node"); + } + + @Override public @Nullable SqlNode getNode() { + return node; + } + } + + /** + * Namespace for a DELETE statement. + */ + private static class DeleteNamespace extends DmlNamespace { + private final SqlDelete node; + + DeleteNamespace(SqlValidatorImpl validator, SqlDelete node, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + super(validator, node.getTargetTable(), enclosingNode, parentScope); + this.node = requireNonNull(node, "node"); + } + + @Override public @Nullable SqlNode getNode() { + return node; + } + } + + /** + * Namespace for a MERGE statement. + */ + private static class MergeNamespace extends DmlNamespace { + private final SqlMerge node; + + MergeNamespace(SqlValidatorImpl validator, SqlMerge node, + SqlNode enclosingNode, SqlValidatorScope parentScope) { + super(validator, node.getTargetTable(), enclosingNode, parentScope); + this.node = requireNonNull(node, "node"); + } + + @Override public @Nullable SqlNode getNode() { + return node; + } + } +} 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 2fe033bedc88..e37b8e54b648 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 @@ -52,6 +52,7 @@ import org.apache.calcite.sql.validate.implicit.TypeCoercions; import org.apiguardian.api.API; +import org.checkerframework.checker.initialization.qual.UnknownInitialization; import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.dataflow.qual.Pure; import org.immutables.value.Value; @@ -883,6 +884,15 @@ Config withSqlQueryScopeFactory( return SqlQueryScopesImpl::new; } + /** Set a factory for name space builder to allow for custom behavior downstream. */ + Config withNamespaceBuilderFactory( + Function<@UnknownInitialization SqlValidatorImpl, NamespaceBuilder> factory); + + /** Returns a NamespaceBuilder factory. */ + @Value.Default default Function<@UnknownInitialization SqlValidatorImpl, NamespaceBuilder> + namespaceBuilderFactory() { + return NamespaceBuilder::new; + } /** Returns the SQL 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 e13c15179c45..4e25b0f13a6e 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 @@ -107,6 +107,7 @@ 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.NamespaceBuilder.DmlNamespace; import org.apache.calcite.sql.validate.SqlQueryScopes.Clause; import org.apache.calcite.sql.validate.implicit.TypeCoercion; import org.apache.calcite.util.BitString; @@ -271,6 +272,7 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { // TypeCoercion instance used for implicit type coercion. private final TypeCoercion typeCoercion; private final SqlQueryScopesImpl sqlQueryScopes; + private final NamespaceBuilder namespaceBuilder; //~ Constructors ----------------------------------------------------------- @@ -313,6 +315,7 @@ protected SqlValidatorImpl( TypeCoercion typeCoercion = config.typeCoercionFactory().create(typeFactory, this); this.typeCoercion = typeCoercion; this.sqlQueryScopes = config.sqlQueryScopeFactory().apply(catalogReader); + this.namespaceBuilder = config.namespaceBuilderFactory().apply(this); if (config.conformance().allowLenientCoercion()) { final SqlTypeCoercionRule rules = @@ -402,7 +405,7 @@ public SqlConformance getConformance() { SelectScope cursorScope = new SelectScope(parentScope, createEmptyScope(), select); sqlQueryScopes.putCursorScope(select, cursorScope); - final SelectNamespace selectNs = createSelectNamespace(select, select); + final SelectNamespace selectNs = namespaceBuilder.createSelectNamespace(select, select); final String alias = SqlValidatorUtil.alias(select, nextGeneratedId++); sqlQueryScopes.registerNamespace(cursorScope, alias, selectNs, false); } @@ -2038,7 +2041,7 @@ private void registerMatchRecognize( boolean forceNullable) { final MatchRecognizeNamespace matchRecognizeNamespace = - createMatchRecognizeNameSpace(call, enclosingNode); + namespaceBuilder.createMatchRecognizeNameSpace(call, enclosingNode); sqlQueryScopes.registerNamespace(usingScope, alias, matchRecognizeNamespace, forceNullable); final MatchRecognizeScope matchRecognizeScope = @@ -2055,12 +2058,6 @@ private void registerMatchRecognize( } } - protected MatchRecognizeNamespace createMatchRecognizeNameSpace( - SqlMatchRecognize call, - SqlNode enclosingNode) { - return new MatchRecognizeNamespace(this, call, enclosingNode); - } - private void registerPivot( SqlValidatorScope parentScope, SqlValidatorScope usingScope, @@ -2068,8 +2065,7 @@ private void registerPivot( SqlNode enclosingNode, @Nullable String alias, boolean forceNullable) { - final PivotNamespace namespace = - createPivotNameSpace(pivot, enclosingNode); + final PivotNamespace namespace = namespaceBuilder.createPivotNameSpace(pivot, enclosingNode); sqlQueryScopes.registerNamespace(usingScope, alias, namespace, forceNullable); final SqlValidatorScope scope = @@ -2086,11 +2082,6 @@ private void registerPivot( } } - protected PivotNamespace createPivotNameSpace(SqlPivot call, - SqlNode enclosingNode) { - return new PivotNamespace(this, call, enclosingNode); - } - private void registerUnpivot( SqlValidatorScope parentScope, SqlValidatorScope usingScope, @@ -2099,7 +2090,7 @@ private void registerUnpivot( @Nullable String alias, boolean forceNullable) { final UnpivotNamespace namespace = - createUnpivotNameSpace(call, enclosingNode); + namespaceBuilder.createUnpivotNameSpace(call, enclosingNode); sqlQueryScopes.registerNamespace(usingScope, alias, namespace, forceNullable); final SqlValidatorScope scope = @@ -2116,12 +2107,6 @@ private void registerUnpivot( } } - protected UnpivotNamespace createUnpivotNameSpace(SqlUnpivot call, - SqlNode enclosingNode) { - return new UnpivotNamespace(this, call, enclosingNode); - } - - /** * Registers scopes and namespaces implied a relational expression in the * FROM clause. @@ -2543,35 +2528,6 @@ protected boolean shouldAllowOverRelation() { return false; } - /** - * Creates a namespace for a SELECT node. Derived class may - * override this factory method. - * - * @param select Select node - * @param enclosingNode Enclosing node - * @return Select namespace - */ - protected SelectNamespace createSelectNamespace( - SqlSelect select, - SqlNode enclosingNode) { - return new SelectNamespace(this, select, enclosingNode); - } - - /** - * Creates a namespace for a set operation (UNION, - * INTERSECT, or EXCEPT). Derived class may override - * this factory method. - * - * @param call Call to set operation - * @param enclosingNode Enclosing node - * @return Set operation namespace - */ - protected SetopNamespace createSetopNamespace( - SqlCall call, - SqlNode enclosingNode) { - return new SetopNamespace(this, call, enclosingNode); - } - /** * Registers a query in a parent scope. * @@ -2630,7 +2586,7 @@ private void registerQuery( case SELECT: final SqlSelect select = (SqlSelect) node; final SelectNamespace selectNs = - createSelectNamespace(select, enclosingNode); + namespaceBuilder.createSelectNamespace(select, enclosingNode); sqlQueryScopes.registerNamespace(usingScope, alias, selectNs, forceNullable); final SqlValidatorScope windowParentScope = first(usingScope, parentScope); @@ -2799,9 +2755,8 @@ private void registerQuery( case INSERT: SqlInsert insertCall = (SqlInsert) node; - InsertNamespace insertNs = - new InsertNamespace( - this, + DmlNamespace insertNs = + namespaceBuilder.createInsertNamespace( insertCall, enclosingNode, parentScope); @@ -2817,9 +2772,8 @@ private void registerQuery( case DELETE: SqlDelete deleteCall = (SqlDelete) node; - DeleteNamespace deleteNs = - new DeleteNamespace( - this, + DmlNamespace deleteNs = + namespaceBuilder.createDeleteNamespace( deleteCall, enclosingNode, parentScope); @@ -2839,9 +2793,8 @@ private void registerQuery( node.getParserPosition()); } SqlUpdate updateCall = (SqlUpdate) node; - UpdateNamespace updateNs = - new UpdateNamespace( - this, + DmlNamespace updateNs = + namespaceBuilder.createUpdate( updateCall, enclosingNode, parentScope); @@ -2858,9 +2811,8 @@ private void registerQuery( case MERGE: validateFeature(RESOURCE.sQLFeature_F312(), node.getParserPosition()); SqlMerge mergeCall = (SqlMerge) node; - MergeNamespace mergeNs = - new MergeNamespace( - this, + DmlNamespace mergeNs = + namespaceBuilder.createMergeNamespace( mergeCall, enclosingNode, parentScope); @@ -2961,7 +2913,7 @@ private void registerSetop( boolean forceNullable) { SqlCall call = (SqlCall) node; final SetopNamespace setopNamespace = - createSetopNamespace(call, enclosingNode); + namespaceBuilder.createSetopNamespace(call, enclosingNode); sqlQueryScopes.registerNamespace(usingScope, alias, setopNamespace, forceNullable); // A setop is in the same scope as its parent. @@ -6566,84 +6518,6 @@ private static boolean isSingleVarRequired(SqlKind kind) { //~ Inner Classes ---------------------------------------------------------- - /** - * Common base class for DML statement namespaces. - */ - public static class DmlNamespace extends IdentifierNamespace { - protected DmlNamespace(SqlValidatorImpl validator, SqlNode id, - SqlNode enclosingNode, SqlValidatorScope parentScope) { - super(validator, id, enclosingNode, parentScope); - } - } - - /** - * Namespace for an INSERT statement. - */ - private static class InsertNamespace extends DmlNamespace { - private final SqlInsert node; - - InsertNamespace(SqlValidatorImpl validator, SqlInsert node, - SqlNode enclosingNode, SqlValidatorScope parentScope) { - super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = requireNonNull(node, "node"); - } - - @Override public @Nullable SqlNode getNode() { - return node; - } - } - - /** - * Namespace for an UPDATE statement. - */ - private static class UpdateNamespace extends DmlNamespace { - private final SqlUpdate node; - - UpdateNamespace(SqlValidatorImpl validator, SqlUpdate node, - SqlNode enclosingNode, SqlValidatorScope parentScope) { - super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = requireNonNull(node, "node"); - } - - @Override public @Nullable SqlNode getNode() { - return node; - } - } - - /** - * Namespace for a DELETE statement. - */ - private static class DeleteNamespace extends DmlNamespace { - private final SqlDelete node; - - DeleteNamespace(SqlValidatorImpl validator, SqlDelete node, - SqlNode enclosingNode, SqlValidatorScope parentScope) { - super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = requireNonNull(node, "node"); - } - - @Override public @Nullable SqlNode getNode() { - return node; - } - } - - /** - * Namespace for a MERGE statement. - */ - private static class MergeNamespace extends DmlNamespace { - private final SqlMerge node; - - MergeNamespace(SqlValidatorImpl validator, SqlMerge node, - SqlNode enclosingNode, SqlValidatorScope parentScope) { - super(validator, node.getTargetTable(), enclosingNode, parentScope); - this.node = requireNonNull(node, "node"); - } - - @Override public @Nullable SqlNode getNode() { - return node; - } - } - /** Visitor that retrieves pattern variables defined. */ private static class PatternVarVisitor implements SqlVisitor { private final MatchRecognizeScope scope; diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java index ac038e3dae69..bab29921d536 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorUtil.java @@ -58,6 +58,7 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.apache.calcite.sql.validate.NamespaceBuilder.DmlNamespace; import org.apache.calcite.util.ImmutableBitSet; import org.apache.calcite.util.Litmus; import org.apache.calcite.util.Pair; @@ -124,9 +125,9 @@ private SqlValidatorUtil() {} return getRelOptTable(tableNamespace, requireNonNull(catalogReader, "catalogReader"), datasetName, usedDataset, tableNamespace.extendedFields); - } else if (namespace.isWrapperFor(SqlValidatorImpl.DmlNamespace.class)) { - final SqlValidatorImpl.DmlNamespace dmlNamespace = - namespace.unwrap(SqlValidatorImpl.DmlNamespace.class); + } else if (namespace.isWrapperFor(DmlNamespace.class)) { + final DmlNamespace dmlNamespace = + namespace.unwrap(DmlNamespace.class); final SqlValidatorNamespace resolvedNamespace = dmlNamespace.resolve(); if (resolvedNamespace.isWrapperFor(TableNamespace.class)) { final TableNamespace tableNamespace = resolvedNamespace.unwrap(TableNamespace.class); 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 23a8af7e81c2..50771d0a9d5a 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java @@ -163,6 +163,7 @@ import org.apache.calcite.sql.validate.ListScope; import org.apache.calcite.sql.validate.MatchRecognizeScope; import org.apache.calcite.sql.validate.MeasureScope; +import org.apache.calcite.sql.validate.NamespaceBuilder.DmlNamespace; import org.apache.calcite.sql.validate.ParameterScope; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlLambdaScope; @@ -4112,8 +4113,8 @@ public RelNode toRel(final RelOptTable table, final List hints) { protected RelOptTable getTargetTable(SqlNode call) { final SqlValidatorNamespace targetNs = getNamespace(call); SqlValidatorNamespace namespace; - if (targetNs.isWrapperFor(SqlValidatorImpl.DmlNamespace.class)) { - namespace = targetNs.unwrap(SqlValidatorImpl.DmlNamespace.class); + if (targetNs.isWrapperFor(DmlNamespace.class)) { + namespace = targetNs.unwrap(DmlNamespace.class); } else { namespace = targetNs.resolve(); }