MutableRel.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.mutable;
import org.apache.calcite.avatica.util.Spaces;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.type.RelDataType;
import com.google.common.base.Equivalence;
import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import static java.util.Objects.requireNonNull;
/** Mutable equivalent of {@link RelNode}.
*
* <p>Each node has mutable state, and keeps track of its parent and position
* within parent.
* It doesn't make sense to canonize {@code MutableRels},
* otherwise one node could end up with multiple parents.
* It follows that {@code #hashCode} and {@code #equals} are less efficient
* than their {@code RelNode} counterparts.
* But, you don't need to copy a {@code MutableRel} in order to change it.
* For this reason, you should use {@code MutableRel} for short-lived
* operations, and transcribe back to {@code RelNode} when you are done.
*/
public abstract class MutableRel {
/** Equivalence that compares objects by their {@link Object#toString()}
* method. */
protected static final Equivalence<Object> STRING_EQUIVALENCE =
new Equivalence<Object>() {
@Override protected boolean doEquivalent(Object o, Object o2) {
return o.toString().equals(o2.toString());
}
@Override protected int doHash(Object o) {
return o.toString().hashCode();
}
};
/** Equivalence that compares {@link Lists}s by the
* {@link Object#toString()} of their elements. */
@SuppressWarnings("unchecked")
protected static final Equivalence<List<?>> PAIRWISE_STRING_EQUIVALENCE =
(Equivalence) STRING_EQUIVALENCE.pairwise();
public final RelOptCluster cluster;
public final RelDataType rowType;
protected final MutableRelType type;
protected @Nullable MutableRel parent;
protected int ordinalInParent;
protected MutableRel(RelOptCluster cluster,
RelDataType rowType, MutableRelType type) {
this.cluster = requireNonNull(cluster, "cluster");
this.rowType = requireNonNull(rowType, "rowType");
this.type = requireNonNull(type, "type");
}
public @Nullable MutableRel getParent() {
return parent;
}
public abstract void setInput(int ordinalInParent, MutableRel input);
public abstract List<MutableRel> getInputs();
@Override public abstract MutableRel clone();
public abstract void childrenAccept(MutableRelVisitor visitor);
/** Replaces this {@code MutableRel} in its parent with another node at the
* same position.
*
* <p>Before the method, {@code child} must be an orphan (have null parent)
* and after this method, this {@code MutableRel} is an orphan.
*
* @return The parent
*/
public @Nullable MutableRel replaceInParent(MutableRel child) {
final MutableRel parent = this.parent;
if (this != child) {
if (parent != null) {
parent.setInput(ordinalInParent, child);
this.parent = null;
this.ordinalInParent = 0;
}
}
return parent;
}
public abstract StringBuilder digest(StringBuilder buf);
public final String deep() {
return new MutableRelDumper().apply(this);
}
@Override public final String toString() {
return deep();
}
/**
* Implementation of MutableVisitor that dumps the details
* of a MutableRel tree.
*/
private static class MutableRelDumper extends MutableRelVisitor {
private final StringBuilder buf = new StringBuilder();
private int level;
@Override public void visit(@Nullable MutableRel node) {
Spaces.append(buf, level * 2);
if (node == null) {
buf.append("null");
} else {
node.digest(buf);
buf.append("\n");
++level;
super.visit(node);
--level;
}
}
public String apply(MutableRel rel) {
go(rel);
return buf.toString();
}
}
}