blob: eaec60a401d0bf94764fdf295566eecdace48e6e [file] [log] [blame]
/*
* 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.plan;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.CorrelationId;
import org.apache.calcite.rel.hint.HintStrategyTable;
import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
import org.apache.calcite.rel.metadata.JaninoRelMetadataProvider;
import org.apache.calcite.rel.metadata.MetadataFactory;
import org.apache.calcite.rel.metadata.RelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.metadata.RelMetadataQueryBase;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static org.apache.calcite.linq4j.Nullness.castNonNull;
/**
* An environment for related relational expressions during the
* optimization of a query.
*/
public class RelOptCluster {
//~ Instance fields --------------------------------------------------------
private final RelDataTypeFactory typeFactory;
private final RelOptPlanner planner;
private final AtomicInteger nextCorrel;
private final Map<String, RelNode> mapCorrelToRel;
private RexNode originalExpression;
private final RexBuilder rexBuilder;
private RelMetadataProvider metadataProvider;
@Deprecated // to be removed before 2.0
private MetadataFactory metadataFactory;
private @Nullable HintStrategyTable hintStrategies;
private final RelTraitSet emptyTraitSet;
private @Nullable RelMetadataQuery mq;
private Supplier<RelMetadataQuery> mqSupplier;
//~ Constructors -----------------------------------------------------------
/**
* Creates a cluster.
*/
@Deprecated // to be removed before 2.0
RelOptCluster(
RelOptQuery query,
RelOptPlanner planner,
RelDataTypeFactory typeFactory,
RexBuilder rexBuilder) {
this(planner, typeFactory, rexBuilder, query.nextCorrel,
query.mapCorrelToRel);
}
/**
* Creates a cluster.
*
* <p>For use only from {@link #create} and {@link RelOptQuery}.
*/
RelOptCluster(RelOptPlanner planner, RelDataTypeFactory typeFactory,
RexBuilder rexBuilder, AtomicInteger nextCorrel,
Map<String, RelNode> mapCorrelToRel) {
this.nextCorrel = nextCorrel;
this.mapCorrelToRel = mapCorrelToRel;
this.planner = Objects.requireNonNull(planner, "planner");
this.typeFactory = Objects.requireNonNull(typeFactory, "typeFactory");
this.rexBuilder = rexBuilder;
this.originalExpression = rexBuilder.makeLiteral("?");
// set up a default rel metadata provider,
// giving the planner first crack at everything
setMetadataProvider(DefaultRelMetadataProvider.INSTANCE);
setMetadataQuerySupplier(RelMetadataQuery::instance);
this.emptyTraitSet = planner.emptyTraitSet();
assert emptyTraitSet.size() == planner.getRelTraitDefs().size();
}
/** Creates a cluster. */
public static RelOptCluster create(RelOptPlanner planner,
RexBuilder rexBuilder) {
return new RelOptCluster(planner, rexBuilder.getTypeFactory(),
rexBuilder, new AtomicInteger(0), new HashMap<>());
}
//~ Methods ----------------------------------------------------------------
@Deprecated // to be removed before 2.0
public RelOptQuery getQuery() {
return new RelOptQuery(castNonNull(planner), nextCorrel, mapCorrelToRel);
}
@Deprecated // to be removed before 2.0
public RexNode getOriginalExpression() {
return originalExpression;
}
@Deprecated // to be removed before 2.0
public void setOriginalExpression(RexNode originalExpression) {
this.originalExpression = originalExpression;
}
public RelOptPlanner getPlanner() {
return planner;
}
public RelDataTypeFactory getTypeFactory() {
return typeFactory;
}
public RexBuilder getRexBuilder() {
return rexBuilder;
}
public @Nullable RelMetadataProvider getMetadataProvider() {
return metadataProvider;
}
/**
* Overrides the default metadata provider for this cluster.
*
* @param metadataProvider custom provider
*/
@EnsuresNonNull({"this.metadataProvider", "this.metadataFactory"})
@SuppressWarnings("deprecation")
public void setMetadataProvider(
@UnknownInitialization RelOptCluster this,
RelMetadataProvider metadataProvider) {
this.metadataProvider = metadataProvider;
this.metadataFactory =
new org.apache.calcite.rel.metadata.MetadataFactoryImpl(metadataProvider);
// Wrap the metadata provider as a JaninoRelMetadataProvider
// and set it to the ThreadLocal,
// JaninoRelMetadataProvider is required by the RelMetadataQuery.
RelMetadataQueryBase.THREAD_PROVIDERS
.set(JaninoRelMetadataProvider.of(metadataProvider));
}
/**
* Returns a {@link MetadataFactory}.
*
* @deprecated Use {@link #getMetadataQuery()}.
*/
@Deprecated // to be removed before 2.0
public MetadataFactory getMetadataFactory() {
return metadataFactory;
}
/**
* Sets up the customized {@link RelMetadataQuery} instance supplier that to
* use during rule planning.
*
* <p>Note that the {@code mqSupplier} should return
* a fresh new {@link RelMetadataQuery} instance because the instance would be
* cached in this cluster, and we may invalidate and re-generate it
* for each {@link RelOptRuleCall} cycle.
*/
@EnsuresNonNull("this.mqSupplier")
public void setMetadataQuerySupplier(
@UnknownInitialization RelOptCluster this,
Supplier<RelMetadataQuery> mqSupplier) {
this.mqSupplier = mqSupplier;
}
/**
* Returns the current RelMetadataQuery.
*
* <p>This method might be changed or moved in future.
* If you have a {@link RelOptRuleCall} available,
* for example if you are in a {@link RelOptRule#onMatch(RelOptRuleCall)}
* method, then use {@link RelOptRuleCall#getMetadataQuery()} instead. */
public RelMetadataQuery getMetadataQuery() {
if (mq == null) {
mq = castNonNull(mqSupplier).get();
}
return mq;
}
/**
* Returns the supplier of RelMetadataQuery.
*/
public Supplier<RelMetadataQuery> getMetadataQuerySupplier() {
return this.mqSupplier;
}
/**
* Should be called whenever the current {@link RelMetadataQuery} becomes
* invalid. Typically invoked from {@link RelOptRuleCall#transformTo}.
*/
public void invalidateMetadataQuery() {
mq = null;
}
/**
* Sets up the hint propagation strategies to be used during rule planning.
*
* <p>Use <code>RelOptNode.getCluster().getHintStrategies()</code> to fetch
* the hint strategies.
*
* <p>Note that this method is only for internal use; the cluster {@code hintStrategies}
* would be always set up with the instance configured by
* {@link org.apache.calcite.sql2rel.SqlToRelConverter.Config}.
*
* @param hintStrategies The specified hint strategies to override the default one(empty)
*/
public void setHintStrategies(HintStrategyTable hintStrategies) {
Objects.requireNonNull(hintStrategies, "hintStrategies");
this.hintStrategies = hintStrategies;
}
/**
* Returns the hint strategies of this cluster. It is immutable during the whole planning phrase.
*/
public HintStrategyTable getHintStrategies() {
if (this.hintStrategies == null) {
this.hintStrategies = HintStrategyTable.EMPTY;
}
return this.hintStrategies;
}
/**
* Constructs a new id for a correlating variable. It is unique within the
* whole query.
*/
public CorrelationId createCorrel() {
return new CorrelationId(nextCorrel.getAndIncrement());
}
/** Returns the default trait set for this cluster. */
public RelTraitSet traitSet() {
return emptyTraitSet;
}
// CHECKSTYLE: IGNORE 2
/** @deprecated For {@code traitSetOf(t1, t2)},
* use {@link #traitSet}().replace(t1).replace(t2). */
@Deprecated // to be removed before 2.0
public RelTraitSet traitSetOf(RelTrait... traits) {
RelTraitSet traitSet = emptyTraitSet;
for (RelTrait trait : traits) {
traitSet = traitSet.replace(trait);
}
return traitSet;
}
public RelTraitSet traitSetOf(RelTrait trait) {
return emptyTraitSet.replace(trait);
}
}