Calc.java
/*
* 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.rel.core;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelWriter;
import org.apache.calcite.rel.SingleRel;
import org.apache.calcite.rel.hint.Hintable;
import org.apache.calcite.rel.hint.RelHint;
import org.apache.calcite.rel.metadata.RelMdUtil;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexLocalRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexOver;
import org.apache.calcite.rex.RexProgram;
import org.apache.calcite.rex.RexProgramBuilder;
import org.apache.calcite.rex.RexShuttle;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.util.Litmus;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
/**
* <code>Calc</code> is an abstract base class for implementations of
* {@link org.apache.calcite.rel.logical.LogicalCalc}.
*/
public abstract class Calc extends SingleRel implements Hintable {
//~ Instance fields --------------------------------------------------------
protected final ImmutableList<RelHint> hints;
protected final RexProgram program;
//~ Constructors -----------------------------------------------------------
/**
* Creates a Calc.
*
* @param cluster Cluster
* @param traits Traits
* @param hints Hints of this relational expression
* @param child Input relation
* @param program Calc program
*/
@SuppressWarnings("method.invocation.invalid")
protected Calc(
RelOptCluster cluster,
RelTraitSet traits,
List<RelHint> hints,
RelNode child,
RexProgram program) {
super(cluster, traits, child);
this.rowType = program.getOutputRowType();
this.program = program;
this.hints = ImmutableList.copyOf(hints);
assert isValid(Litmus.THROW, null);
}
@Deprecated // to be removed before 2.0
protected Calc(
RelOptCluster cluster,
RelTraitSet traits,
RelNode child,
RexProgram program) {
this(cluster, traits, ImmutableList.of(), child, program);
}
@Deprecated // to be removed before 2.0
protected Calc(
RelOptCluster cluster,
RelTraitSet traits,
RelNode child,
RexProgram program,
List<RelCollation> collationList) {
this(cluster, traits, ImmutableList.of(), child, program);
Util.discard(collationList);
}
//~ Methods ----------------------------------------------------------------
@Override public final Calc copy(RelTraitSet traitSet, List<RelNode> inputs) {
return copy(traitSet, sole(inputs), program);
}
/**
* Creates a copy of this {@code Calc}.
*
* @param traitSet Traits
* @param child Input relation
* @param program Calc program
* @return New {@code Calc} if any parameter differs from the value of this
* {@code Calc}, or just {@code this} if all the parameters are the same
*
* @see #copy(org.apache.calcite.plan.RelTraitSet, java.util.List)
*/
public abstract Calc copy(
RelTraitSet traitSet,
RelNode child,
RexProgram program);
@Deprecated // to be removed before 2.0
public Calc copy(
RelTraitSet traitSet,
RelNode child,
RexProgram program,
List<RelCollation> collationList) {
Util.discard(collationList);
return copy(traitSet, child, program);
}
/** Returns whether this Calc contains any windowed-aggregate functions. */
public final boolean containsOver() {
return RexOver.containsOver(program);
}
@Override public boolean isValid(Litmus litmus, @Nullable Context context) {
if (!RelOptUtil.equal(
"program's input type",
program.getInputRowType(),
"child's output type",
getInput().getRowType(), litmus)) {
return litmus.fail(null);
}
if (!program.isValid(litmus, context)) {
return litmus.fail(null);
}
if (!program.isNormalized(litmus, getCluster().getRexBuilder())) {
return litmus.fail(null);
}
if (RexUtil.M2V_FINDER.inProgram(program)) {
return litmus.fail("program contains M2V");
}
return litmus.succeed();
}
public RexProgram getProgram() {
return program;
}
@Override public ImmutableList<RelHint> getHints() {
return hints;
}
@Override public double estimateRowCount(RelMetadataQuery mq) {
return RelMdUtil.estimateFilteredRows(getInput(), program, mq);
}
@Override public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner,
RelMetadataQuery mq) {
double dRows = mq.getRowCount(this);
double dCpu = mq.getRowCount(getInput())
* program.getExprCount();
double dIo = 0;
return planner.getCostFactory().makeCost(dRows, dCpu, dIo);
}
@Override public RelWriter explainTerms(RelWriter pw) {
return program.explainCalc(super.explainTerms(pw));
}
@Override public RelNode accept(RexShuttle shuttle) {
List<RexNode> oldExprs = program.getExprList();
List<RexNode> exprs = shuttle.apply(oldExprs);
List<RexLocalRef> oldProjects = program.getProjectList();
List<RexLocalRef> projects = shuttle.apply(oldProjects);
RexLocalRef oldCondition = program.getCondition();
RexNode condition;
if (oldCondition != null) {
condition = shuttle.apply(oldCondition);
assert condition instanceof RexLocalRef
: "Invalid condition after rewrite. Expected RexLocalRef, got "
+ condition;
} else {
condition = null;
}
if (exprs == oldExprs
&& projects == oldProjects
&& condition == oldCondition) {
return this;
}
final RexBuilder rexBuilder = getCluster().getRexBuilder();
final RelDataType rowType =
RexUtil.createStructType(
rexBuilder.getTypeFactory(),
projects,
getRowType().getFieldNames(),
null);
final RexProgram newProgram =
RexProgramBuilder.create(
rexBuilder, program.getInputRowType(), exprs, projects,
condition, rowType, true, null)
.getProgram(false);
return copy(traitSet, getInput(), newProgram);
}
}