/* * 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.SortOrder; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.google.common.collect.ImmutableList; import com.google.common.primitives.Ints; import java.util.List; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class OrderByOperator implements Operator { public static class OrderByOperatorFactory implements OperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; private final List<Type> sourceTypes; private final List<Integer> outputChannels; private final int expectedPositions; private final List<Integer> sortChannels; private final List<SortOrder> sortOrder; private final List<Type> types; private boolean closed; private final PagesIndex.Factory pagesIndexFactory; public OrderByOperatorFactory( int operatorId, PlanNodeId planNodeId, List<? extends Type> sourceTypes, List<Integer> outputChannels, int expectedPositions, List<Integer> sortChannels, List<SortOrder> sortOrder, PagesIndex.Factory pagesIndexFactory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); this.sourceTypes = ImmutableList.copyOf(requireNonNull(sourceTypes, "sourceTypes is null")); this.outputChannels = requireNonNull(outputChannels, "outputChannels is null"); this.expectedPositions = expectedPositions; this.sortChannels = ImmutableList.copyOf(requireNonNull(sortChannels, "sortChannels is null")); this.sortOrder = ImmutableList.copyOf(requireNonNull(sortOrder, "sortOrder is null")); this.types = toTypes(sourceTypes, outputChannels); this.pagesIndexFactory = requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); } @Override public List<Type> getTypes() { return types; } @Override public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, OrderByOperator.class.getSimpleName()); return new OrderByOperator( operatorContext, sourceTypes, outputChannels, expectedPositions, sortChannels, sortOrder, pagesIndexFactory); } @Override public void close() { closed = true; } @Override public OperatorFactory duplicate() { return new OrderByOperatorFactory(operatorId, planNodeId, sourceTypes, outputChannels, expectedPositions, sortChannels, sortOrder, pagesIndexFactory); } } private enum State { NEEDS_INPUT, HAS_OUTPUT, FINISHED } private final OperatorContext operatorContext; private final List<Integer> sortChannels; private final List<SortOrder> sortOrder; private final int[] outputChannels; private final List<Type> types; private final PagesIndex pageIndex; private final PageBuilder pageBuilder; private int currentPosition; private State state = State.NEEDS_INPUT; public OrderByOperator( OperatorContext operatorContext, List<Type> sourceTypes, List<Integer> outputChannels, int expectedPositions, List<Integer> sortChannels, List<SortOrder> sortOrder, PagesIndex.Factory pagesIndexFactory) { requireNonNull(pagesIndexFactory, "pagesIndexFactory is null"); this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.outputChannels = Ints.toArray(requireNonNull(outputChannels, "outputChannels is null")); this.types = toTypes(sourceTypes, outputChannels); this.sortChannels = ImmutableList.copyOf(requireNonNull(sortChannels, "sortChannels is null")); this.sortOrder = ImmutableList.copyOf(requireNonNull(sortOrder, "sortOrder is null")); this.pageIndex = pagesIndexFactory.newPagesIndex(sourceTypes, expectedPositions); this.pageBuilder = new PageBuilder(this.types); } @Override public OperatorContext getOperatorContext() { return operatorContext; } @Override public List<Type> getTypes() { return types; } @Override public void finish() { if (state == State.NEEDS_INPUT) { state = State.HAS_OUTPUT; // sort the index pageIndex.sort(sortChannels, sortOrder); } } @Override public boolean isFinished() { return state == State.FINISHED; } @Override public boolean needsInput() { return state == State.NEEDS_INPUT; } @Override public void addInput(Page page) { checkState(state == State.NEEDS_INPUT, "Operator is already finishing"); requireNonNull(page, "page is null"); pageIndex.addPage(page); if (!operatorContext.trySetMemoryReservation(pageIndex.getEstimatedSize().toBytes())) { pageIndex.compact(); } operatorContext.setMemoryReservation(pageIndex.getEstimatedSize().toBytes()); } @Override public Page getOutput() { if (state != State.HAS_OUTPUT) { return null; } if (currentPosition >= pageIndex.getPositionCount()) { state = State.FINISHED; return null; } // iterate through the positions sequentially until we have one full page pageBuilder.reset(); currentPosition = pageIndex.buildPage(currentPosition, outputChannels, pageBuilder); // output the page if we have any data if (pageBuilder.isEmpty()) { state = State.FINISHED; return null; } Page page = pageBuilder.build(); return page; } private static List<Type> toTypes(List<? extends Type> sourceTypes, List<Integer> outputChannels) { ImmutableList.Builder<Type> types = ImmutableList.builder(); for (int channel : outputChannels) { types.add(sourceTypes.get(channel)); } return types.build(); } }