io.trino.sql.planner.plan.SetOperationNode Maven / Gradle / Ivy
/*
* Licensed 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 io.trino.sql.planner.plan;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.errorprone.annotations.Immutable;
import io.trino.sql.planner.Symbol;
import io.trino.sql.tree.SymbolReference;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
@Immutable
public abstract class SetOperationNode
extends PlanNode
{
private final List sources;
private final ListMultimap outputToInputs;
private final List outputs;
@JsonCreator
protected SetOperationNode(
@JsonProperty("id") PlanNodeId id,
@JsonProperty("sources") List sources,
@JsonProperty("outputToInputs") ListMultimap outputToInputs,
@JsonProperty("outputs") List outputs)
{
super(id);
requireNonNull(sources, "sources is null");
checkArgument(!sources.isEmpty(), "Must have at least one source");
requireNonNull(outputToInputs, "outputToInputs is null");
requireNonNull(outputs, "outputs is null");
this.sources = ImmutableList.copyOf(sources);
this.outputToInputs = ImmutableListMultimap.copyOf(outputToInputs);
this.outputs = ImmutableList.copyOf(outputs);
for (Collection inputs : this.outputToInputs.asMap().values()) {
checkArgument(inputs.size() == this.sources.size(), "Every source needs to map its symbols to an output %s operation symbol", this.getClass().getSimpleName());
}
// Make sure each source positionally corresponds to their Symbol values in the Multimap
for (int i = 0; i < sources.size(); i++) {
for (Collection expectedInputs : this.outputToInputs.asMap().values()) {
checkArgument(sources.get(i).getOutputSymbols().contains(Iterables.get(expectedInputs, i)), "Source does not provide required symbols");
}
}
}
@Override
@JsonProperty("sources")
public List getSources()
{
return sources;
}
@Override
@JsonProperty("outputs")
public List getOutputSymbols()
{
return outputs;
}
@JsonProperty("outputToInputs")
public ListMultimap getSymbolMapping()
{
return outputToInputs;
}
public List sourceOutputLayout(int sourceIndex)
{
// Make sure the sourceOutputLayout symbols are listed in the same order as the corresponding output symbols
return getOutputSymbols().stream()
.map(symbol -> outputToInputs.get(symbol).get(sourceIndex))
.collect(toImmutableList());
}
/**
* Returns the output to input symbol mapping for the given source channel
*/
public Map sourceSymbolMap(int sourceIndex)
{
ImmutableMap.Builder builder = ImmutableMap.builder();
for (Map.Entry> entry : outputToInputs.asMap().entrySet()) {
builder.put(entry.getKey(), Iterables.get(entry.getValue(), sourceIndex).toSymbolReference());
}
return builder.buildOrThrow();
}
/**
* Returns the input to output symbol mapping for the given source channel.
* A single input symbol can map to multiple output symbols, thus requiring a Multimap.
*/
public Multimap outputSymbolMap(int sourceIndex)
{
return Multimaps.transformValues(FluentIterable.from(getOutputSymbols())
.toMap(outputToSourceSymbolFunction(sourceIndex))
.asMultimap()
.inverse(), Symbol::toSymbolReference);
}
private Function outputToSourceSymbolFunction(int sourceIndex)
{
return outputSymbol -> outputToInputs.get(outputSymbol).get(sourceIndex);
}
}