com.facebook.presto.jdbc.internal.spi.plan.TableScanNode 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 com.facebook.presto.jdbc.internal.spi.plan;
import com.facebook.presto.jdbc.internal.common.predicate.TupleDomain;
import com.facebook.presto.jdbc.internal.spi.ColumnHandle;
import com.facebook.presto.jdbc.internal.spi.SourceLocation;
import com.facebook.presto.jdbc.internal.spi.TableHandle;
import com.facebook.presto.jdbc.internal.spi.constraints.TableConstraint;
import com.facebook.presto.jdbc.internal.spi.relation.VariableReferenceExpression;
import com.facebook.presto.jdbc.internal.jackson.annotation.JsonCreator;
import com.facebook.presto.jdbc.internal.jackson.annotation.JsonProperty;
import com.facebook.presto.jdbc.internal.javax.annotation.Nullable;
import com.facebook.presto.jdbc.internal.javax.annotation.concurrent.Immutable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
@Immutable
public final class TableScanNode
extends PlanNode
{
private final TableHandle table;
private final Map assignments;
private final List outputVariables;
// Used during predicate refinement over multiple passes of predicate pushdown
// TODO: think about how to get rid of this in new planner
// TODO: these two fields will not be effective if they are created by connectors until we have refactored PickTableLayout
private final TupleDomain currentConstraint;
private final TupleDomain enforcedConstraint;
private final List> tableConstraints;
/**
* This constructor is for JSON deserialization only. Do not use!
*/
@JsonCreator
public TableScanNode(
Optional sourceLocation,
@JsonProperty("id") PlanNodeId id,
@JsonProperty("table") TableHandle table,
@JsonProperty("outputVariables") List outputVariables,
@JsonProperty("assignments") Map assignments)
{
super(sourceLocation, id, Optional.empty());
this.table = requireNonNull(table, "table is null");
this.outputVariables = unmodifiableList(requireNonNull(outputVariables, "outputVariables is null"));
this.assignments = unmodifiableMap(new HashMap<>(requireNonNull(assignments, "assignments is null")));
checkArgument(assignments.keySet().containsAll(outputVariables), "assignments does not cover all of outputs");
this.currentConstraint = null;
this.enforcedConstraint = null;
this.tableConstraints = emptyList();
}
public TableScanNode(
Optional sourceLocation,
PlanNodeId id,
TableHandle table,
List outputVariables,
Map assignments,
TupleDomain currentConstraint,
TupleDomain enforcedConstraint)
{
this (sourceLocation, id, table, outputVariables, assignments, emptyList(), currentConstraint, enforcedConstraint);
}
public TableScanNode(
Optional sourceLocation,
PlanNodeId id,
TableHandle table,
List outputVariables,
Map assignments,
List> tableConstraints,
TupleDomain currentConstraint,
TupleDomain enforcedConstraint)
{
this (sourceLocation, id, Optional.empty(), table, outputVariables, assignments, tableConstraints, currentConstraint, enforcedConstraint);
}
public TableScanNode(
Optional sourceLocation,
PlanNodeId id,
Optional statsEquivalentPlanNode,
TableHandle table,
List outputVariables,
Map assignments,
List> tableConstraints,
TupleDomain currentConstraint,
TupleDomain enforcedConstraint)
{
super(sourceLocation, id, statsEquivalentPlanNode);
this.table = requireNonNull(table, "table is null");
this.outputVariables = unmodifiableList(requireNonNull(outputVariables, "outputVariables is null"));
this.assignments = unmodifiableMap(new HashMap<>(requireNonNull(assignments, "assignments is null")));
checkArgument(assignments.keySet().containsAll(outputVariables), "assignments does not cover all of outputs");
this.currentConstraint = requireNonNull(currentConstraint, "currentConstraint is null");
this.enforcedConstraint = requireNonNull(enforcedConstraint, "enforcedConstraint is null");
if (!currentConstraint.isAll() || !enforcedConstraint.isAll()) {
checkArgument(table.getLayout().isPresent(), "tableLayout must be present when currentConstraint or enforcedConstraint is non-trivial");
}
this.tableConstraints = requireNonNull(tableConstraints, "tableConstraints is null");
}
/**
* Get the table handle provided by connector
*/
@JsonProperty("table")
public TableHandle getTable()
{
return table;
}
/**
* Get table constraints defined by connector
*/
public List> getTableConstraints()
{
return tableConstraints;
}
/**
* Get the mapping from symbols to columns
*/
@JsonProperty
public Map getAssignments()
{
return assignments;
}
/**
* A TupleDomain that represents a predicate that every row this TableScan node
* produces is guaranteed to satisfy.
*
* This guarantee can have different origins.
* For example, it may be successful predicate push down, or inherent guarantee provided by the underlying data.
*
* currentConstraint will only be used in planner. It is not transported to worker thus will be null on worker.
*/
@Nullable
public TupleDomain getCurrentConstraint()
{
return currentConstraint;
}
/**
* A TupleDomain that represents a predicate that has been successfully pushed into
* this TableScan node. In other words, predicates that were removed from filters
* above the TableScan node because the TableScan node can guarantee it.
*
* This field is used to make sure that predicates which were previously pushed down
* do not get lost in subsequent refinements of the table layout.
*/
public TupleDomain getEnforcedConstraint()
{
// enforcedConstraint can be pretty complex. As a result, it may incur a significant cost to serialize, store, and transport.
checkState(enforcedConstraint != null, "enforcedConstraint should only be used in planner. It is not transported to workers.");
return enforcedConstraint;
}
@Override
public List getSources()
{
// table scan should be the leaf node
return emptyList();
}
@Override
public LogicalProperties computeLogicalProperties(LogicalPropertiesProvider logicalPropertiesProvider)
{
requireNonNull(logicalPropertiesProvider, "logicalPropertiesProvider cannot be null.");
return logicalPropertiesProvider.getTableScanProperties(this);
}
@Override
@JsonProperty
public List getOutputVariables()
{
return outputVariables;
}
@Override
public R accept(PlanVisitor visitor, C context)
{
return visitor.visitTableScan(this, context);
}
@Override
public PlanNode assignStatsEquivalentPlanNode(Optional statsEquivalentPlanNode)
{
return new TableScanNode(getSourceLocation(), getId(), statsEquivalentPlanNode, table, outputVariables, assignments, tableConstraints, currentConstraint, enforcedConstraint);
}
@Override
public String toString()
{
StringBuilder stringBuilder = new StringBuilder(this.getClass().getSimpleName());
stringBuilder.append(" {");
stringBuilder.append("table='").append(table).append('\'');
stringBuilder.append(", outputVariables='").append(outputVariables).append('\'');
stringBuilder.append(", assignments='").append(assignments).append('\'');
stringBuilder.append(", currentConstraint='").append(currentConstraint).append('\'');
stringBuilder.append(", enforcedConstraint='").append(enforcedConstraint).append('\'');
stringBuilder.append('}');
return stringBuilder.toString();
}
@Override
public PlanNode replaceChildren(List newChildren)
{
checkArgument(newChildren.isEmpty(), "newChildren is not empty");
return this;
}
private static void checkArgument(boolean test, String errorMessage)
{
if (!test) {
throw new IllegalArgumentException(errorMessage);
}
}
private static void checkState(boolean test, String errorMessage)
{
if (!test) {
throw new IllegalStateException(errorMessage);
}
}
}