/* * 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.operator; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.connector.ConnectorOutputMetadata; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.google.common.collect.ImmutableList; import io.airlift.slice.Slice; import java.util.Collection; import java.util.List; import java.util.Optional; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class TableFinishOperator implements Operator { public static final List<Type> TYPES = ImmutableList.of(BIGINT); public static class TableFinishOperatorFactory implements OperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; private final TableFinisher tableFinisher; private boolean closed; public TableFinishOperatorFactory(int operatorId, PlanNodeId planNodeId, TableFinisher tableFinisher) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); this.tableFinisher = requireNonNull(tableFinisher, "tableCommitter is null"); } @Override public List<Type> getTypes() { return TYPES; } @Override public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext context = driverContext.addOperatorContext(operatorId, planNodeId, TableFinishOperator.class.getSimpleName()); return new TableFinishOperator(context, tableFinisher); } @Override public void close() { closed = true; } @Override public OperatorFactory duplicate() { return new TableFinishOperatorFactory(operatorId, planNodeId, tableFinisher); } } private enum State { RUNNING, FINISHING, FINISHED } private final OperatorContext operatorContext; private final TableFinisher tableFinisher; private State state = State.RUNNING; private long rowCount; private boolean closed; private Optional<ConnectorOutputMetadata> outputMetadata = Optional.empty(); private final ImmutableList.Builder<Slice> fragmentBuilder = ImmutableList.builder(); public TableFinishOperator(OperatorContext operatorContext, TableFinisher tableFinisher) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.tableFinisher = requireNonNull(tableFinisher, "tableCommitter is null"); operatorContext.setInfoSupplier(() -> new TableFinishInfo(outputMetadata)); } @Override public OperatorContext getOperatorContext() { return operatorContext; } @Override public List<Type> getTypes() { return TYPES; } @Override public void finish() { if (state == State.RUNNING) { state = State.FINISHING; } } @Override public boolean isFinished() { return state == State.FINISHED; } @Override public boolean needsInput() { return state == State.RUNNING; } @Override public void addInput(Page page) { requireNonNull(page, "page is null"); checkState(state == State.RUNNING, "Operator is %s", state); Block rowCountBlock = page.getBlock(0); Block fragmentBlock = page.getBlock(1); for (int position = 0; position < page.getPositionCount(); position++) { if (!rowCountBlock.isNull(position)) { rowCount += BIGINT.getLong(rowCountBlock, position); } if (!fragmentBlock.isNull(position)) { fragmentBuilder.add(VARBINARY.getSlice(fragmentBlock, position)); } } } @Override public Page getOutput() { if (state != State.FINISHING) { return null; } state = State.FINISHED; outputMetadata = tableFinisher.finishTable(fragmentBuilder.build()); PageBuilder page = new PageBuilder(getTypes()); page.declarePosition(); BIGINT.writeLong(page.getBlockBuilder(0), rowCount); return page.build(); } @Override public void close() throws Exception { if (!closed) { closed = true; } } public interface TableFinisher { Optional<ConnectorOutputMetadata> finishTable(Collection<Slice> fragments); } }