io.prestosql.sql.planner.optimizations.BeginTableWrite 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.prestosql.sql.planner.optimizations;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import io.prestosql.Session;
import io.prestosql.execution.warnings.WarningCollector;
import io.prestosql.metadata.Metadata;
import io.prestosql.metadata.TableHandle;
import io.prestosql.sql.planner.PlanNodeIdAllocator;
import io.prestosql.sql.planner.SymbolAllocator;
import io.prestosql.sql.planner.TypeProvider;
import io.prestosql.sql.planner.plan.DeleteNode;
import io.prestosql.sql.planner.plan.ExchangeNode;
import io.prestosql.sql.planner.plan.FilterNode;
import io.prestosql.sql.planner.plan.JoinNode;
import io.prestosql.sql.planner.plan.PlanNode;
import io.prestosql.sql.planner.plan.ProjectNode;
import io.prestosql.sql.planner.plan.SemiJoinNode;
import io.prestosql.sql.planner.plan.SimplePlanRewriter;
import io.prestosql.sql.planner.plan.StatisticsWriterNode;
import io.prestosql.sql.planner.plan.TableFinishNode;
import io.prestosql.sql.planner.plan.TableScanNode;
import io.prestosql.sql.planner.plan.TableWriterNode;
import io.prestosql.sql.planner.plan.TableWriterNode.CreateReference;
import io.prestosql.sql.planner.plan.TableWriterNode.CreateTarget;
import io.prestosql.sql.planner.plan.TableWriterNode.DeleteTarget;
import io.prestosql.sql.planner.plan.TableWriterNode.InsertReference;
import io.prestosql.sql.planner.plan.TableWriterNode.InsertTarget;
import io.prestosql.sql.planner.plan.TableWriterNode.WriterTarget;
import io.prestosql.sql.planner.plan.UnionNode;
import java.util.Optional;
import java.util.Set;
import static com.google.common.base.Preconditions.checkState;
import static io.prestosql.sql.planner.optimizations.QueryCardinalityUtil.isAtMostScalar;
import static io.prestosql.sql.planner.plan.ChildReplacer.replaceChildren;
import static java.util.stream.Collectors.toSet;
/*
* Major HACK alert!!!
*
* This logic should be invoked on query start, not during planning. At that point, the token
* returned by beginCreate/beginInsert should be handed down to tasks in a mapping separate
* from the plan that links plan nodes to the corresponding token.
*/
public class BeginTableWrite
implements PlanOptimizer
{
private final Metadata metadata;
public BeginTableWrite(Metadata metadata)
{
this.metadata = metadata;
}
@Override
public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector)
{
return SimplePlanRewriter.rewriteWith(new Rewriter(session), plan, new Context());
}
private class Rewriter
extends SimplePlanRewriter
{
private final Session session;
public Rewriter(Session session)
{
this.session = session;
}
@Override
public PlanNode visitTableWriter(TableWriterNode node, RewriteContext context)
{
// Part of the plan should be an Optional> and this
// callback can create the table and abort the table creation if the query fails.
WriterTarget writerTarget = context.get().getMaterializedHandle(node.getTarget()).get();
return new TableWriterNode(
node.getId(),
node.getSource().accept(this, context),
writerTarget,
node.getRowCountSymbol(),
node.getFragmentSymbol(),
node.getColumns(),
node.getColumnNames(),
node.getNotNullColumnSymbols(),
node.getPartitioningScheme(),
node.getStatisticsAggregation(),
node.getStatisticsAggregationDescriptor());
}
@Override
public PlanNode visitDelete(DeleteNode node, RewriteContext context)
{
DeleteTarget deleteTarget = (DeleteTarget) context.get().getMaterializedHandle(node.getTarget()).get();
return new DeleteNode(
node.getId(),
rewriteDeleteTableScan(node.getSource(), deleteTarget.getHandle()),
deleteTarget,
node.getRowId(),
node.getOutputSymbols());
}
@Override
public PlanNode visitStatisticsWriterNode(StatisticsWriterNode node, RewriteContext context)
{
PlanNode child = node.getSource();
child = child.accept(this, context);
StatisticsWriterNode.WriteStatisticsHandle analyzeHandle =
new StatisticsWriterNode.WriteStatisticsHandle(metadata.beginStatisticsCollection(session, ((StatisticsWriterNode.WriteStatisticsReference) node.getTarget()).getHandle()));
return new StatisticsWriterNode(
node.getId(),
child,
analyzeHandle,
node.getRowCountSymbol(),
node.isRowCountEnabled(),
node.getDescriptor());
}
@Override
public PlanNode visitTableFinish(TableFinishNode node, RewriteContext context)
{
PlanNode child = node.getSource();
WriterTarget originalTarget = getTarget(child);
WriterTarget newTarget = createWriterTarget(originalTarget);
context.get().addMaterializedHandle(originalTarget, newTarget);
child = child.accept(this, context);
return new TableFinishNode(
node.getId(),
child,
newTarget,
node.getRowCountSymbol(),
node.getStatisticsAggregation(),
node.getStatisticsAggregationDescriptor());
}
public WriterTarget getTarget(PlanNode node)
{
if (node instanceof TableWriterNode) {
return ((TableWriterNode) node).getTarget();
}
if (node instanceof DeleteNode) {
return ((DeleteNode) node).getTarget();
}
if (node instanceof ExchangeNode || node instanceof UnionNode) {
Set writerTargets = node.getSources().stream()
.map(this::getTarget)
.collect(toSet());
return Iterables.getOnlyElement(writerTargets);
}
throw new IllegalArgumentException("Invalid child for TableCommitNode: " + node.getClass().getSimpleName());
}
private WriterTarget createWriterTarget(WriterTarget target)
{
// TODO: begin these operations in pre-execution step, not here
// TODO: we shouldn't need to store the schemaTableName in the handles, but there isn't a good way to pass this around with the current architecture
if (target instanceof CreateReference) {
CreateReference create = (CreateReference) target;
return new CreateTarget(metadata.beginCreateTable(session, create.getCatalog(), create.getTableMetadata(), create.getLayout()), create.getTableMetadata().getTable());
}
if (target instanceof InsertReference) {
InsertReference insert = (InsertReference) target;
return new InsertTarget(metadata.beginInsert(session, insert.getHandle(), insert.getColumns()), metadata.getTableMetadata(session, insert.getHandle()).getTable());
}
if (target instanceof DeleteTarget) {
DeleteTarget delete = (DeleteTarget) target;
return new DeleteTarget(metadata.beginDelete(session, delete.getHandle()), delete.getSchemaTableName());
}
if (target instanceof TableWriterNode.RefreshMaterializedViewReference) {
TableWriterNode.RefreshMaterializedViewReference refreshMV = (TableWriterNode.RefreshMaterializedViewReference) target;
return new TableWriterNode.RefreshMaterializedViewTarget(
refreshMV.getStorageTableHandle(),
metadata.beginRefreshMaterializedView(session, refreshMV.getStorageTableHandle(), refreshMV.getSourceTableHandles()),
metadata.getTableMetadata(session, refreshMV.getStorageTableHandle()).getTable(),
refreshMV.getSourceTableHandles());
}
throw new IllegalArgumentException("Unhandled target type: " + target.getClass().getSimpleName());
}
private PlanNode rewriteDeleteTableScan(PlanNode node, TableHandle handle)
{
if (node instanceof TableScanNode) {
TableScanNode scan = (TableScanNode) node;
return new TableScanNode(
scan.getId(),
handle,
scan.getOutputSymbols(),
scan.getAssignments(),
scan.getEnforcedConstraint(),
scan.isForDelete());
}
if (node instanceof FilterNode) {
PlanNode source = rewriteDeleteTableScan(((FilterNode) node).getSource(), handle);
return replaceChildren(node, ImmutableList.of(source));
}
if (node instanceof ProjectNode) {
PlanNode source = rewriteDeleteTableScan(((ProjectNode) node).getSource(), handle);
return replaceChildren(node, ImmutableList.of(source));
}
if (node instanceof SemiJoinNode) {
PlanNode source = rewriteDeleteTableScan(((SemiJoinNode) node).getSource(), handle);
return replaceChildren(node, ImmutableList.of(source, ((SemiJoinNode) node).getFilteringSource()));
}
if (node instanceof JoinNode) {
JoinNode joinNode = (JoinNode) node;
if (joinNode.getType() == JoinNode.Type.INNER && isAtMostScalar(joinNode.getRight())) {
PlanNode source = rewriteDeleteTableScan(joinNode.getLeft(), handle);
return replaceChildren(node, ImmutableList.of(source, joinNode.getRight()));
}
}
throw new IllegalArgumentException("Invalid descendant for DeleteNode: " + node.getClass().getName());
}
}
public static class Context
{
private Optional handle = Optional.empty();
private Optional materializedHandle = Optional.empty();
public void addMaterializedHandle(WriterTarget handle, WriterTarget materializedHandle)
{
checkState(this.handle.isEmpty(), "can only have one WriterTarget in a subtree");
this.handle = Optional.of(handle);
this.materializedHandle = Optional.of(materializedHandle);
}
public Optional getMaterializedHandle(WriterTarget handle)
{
checkState(this.handle.get().equals(handle), "can't find materialized handle for WriterTarget");
return materializedHandle;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy