/* * 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.operator.LookupJoinOperators.JoinType; import com.facebook.presto.operator.LookupOuterOperator.LookupOuterOperatorFactory; import com.facebook.presto.operator.LookupSource.OuterPositionIterator; 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.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import java.util.List; import java.util.Optional; import java.util.function.Consumer; import static com.facebook.presto.operator.LookupJoinOperators.JoinType.INNER; import static com.facebook.presto.operator.LookupJoinOperators.JoinType.PROBE_OUTER; import static com.google.common.base.Preconditions.checkState; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Objects.requireNonNull; public class LookupJoinOperatorFactory implements JoinOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; private final List<Type> probeTypes; private final List<Type> probeOutputTypes; private final List<Type> buildTypes; private final List<Type> buildOutputTypes; private final JoinType joinType; private final LookupSourceFactory lookupSourceFactory; private final JoinProbeFactory joinProbeFactory; private final Optional<OperatorFactory> outerOperatorFactory; private final ReferenceCount referenceCount; private boolean closed; public LookupJoinOperatorFactory(int operatorId, PlanNodeId planNodeId, LookupSourceFactory lookupSourceFactory, List<Type> probeTypes, List<Type> probeOutputTypes, JoinType joinType, JoinProbeFactory joinProbeFactory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); this.lookupSourceFactory = requireNonNull(lookupSourceFactory, "lookupSourceFactory is null"); this.probeTypes = ImmutableList.copyOf(requireNonNull(probeTypes, "probeTypes is null")); this.probeOutputTypes = ImmutableList.copyOf(requireNonNull(probeOutputTypes, "probeOutputTypes is null")); this.buildTypes = ImmutableList.copyOf(lookupSourceFactory.getTypes()); this.buildOutputTypes = ImmutableList.copyOf(lookupSourceFactory.getOutputTypes()); this.joinType = requireNonNull(joinType, "joinType is null"); this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); this.referenceCount = new ReferenceCount(); if (joinType == INNER || joinType == PROBE_OUTER) { // when all join operators finish, destroy the lookup source (freeing the memory) this.referenceCount.getFreeFuture().addListener(lookupSourceFactory::destroy, directExecutor()); this.outerOperatorFactory = Optional.empty(); } else { // when all join operators finish, set the outer position future to start the outer operator SettableFuture<OuterPositionIterator> outerPositionsFuture = SettableFuture.create(); this.referenceCount.getFreeFuture().addListener(() -> { // lookup source may not be finished yet, so add a listener Futures.addCallback( lookupSourceFactory.createLookupSource(), new OnSuccessFutureCallback<>(lookupSource -> outerPositionsFuture.set(lookupSource.getOuterPositionIterator()))); }, directExecutor()); // when output operator finishes, destroy the lookup source Runnable onOperatorClose = () -> { // lookup source may not be finished yet, so add a listener, to free the memory lookupSourceFactory.createLookupSource().addListener(lookupSourceFactory::destroy, directExecutor()); }; this.outerOperatorFactory = Optional.of(new LookupOuterOperatorFactory(operatorId, planNodeId, outerPositionsFuture, probeOutputTypes, buildOutputTypes, onOperatorClose)); } } private LookupJoinOperatorFactory(LookupJoinOperatorFactory other) { requireNonNull(other, "other is null"); operatorId = other.operatorId; planNodeId = other.planNodeId; probeTypes = other.probeTypes; probeOutputTypes = other.probeOutputTypes; buildTypes = other.buildTypes; buildOutputTypes = other.buildOutputTypes; joinType = other.joinType; lookupSourceFactory = other.lookupSourceFactory; joinProbeFactory = other.joinProbeFactory; referenceCount = other.referenceCount; outerOperatorFactory = other.outerOperatorFactory; referenceCount.retain(); } public int getOperatorId() { return operatorId; } @Override public List<Type> getTypes() { return ImmutableList.<Type>builder() .addAll(probeOutputTypes) .addAll(buildOutputTypes) .build(); } @Override public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, LookupJoinOperator.class.getSimpleName()); lookupSourceFactory.setTaskContext(driverContext.getPipelineContext().getTaskContext()); referenceCount.retain(); return new LookupJoinOperator( operatorContext, getTypes(), joinType, lookupSourceFactory.createLookupSource(), joinProbeFactory, referenceCount::release); } @Override public void close() { if (closed) { return; } closed = true; referenceCount.release(); } @Override public OperatorFactory duplicate() { return new LookupJoinOperatorFactory(this); } @Override public Optional<OperatorFactory> createOuterOperatorFactory() { return outerOperatorFactory; } // We use a public class to avoid access problems with the isolated class loaders public static class OnSuccessFutureCallback<T> implements FutureCallback<T> { private final Consumer<T> onSuccess; public OnSuccessFutureCallback(Consumer<T> onSuccess) { this.onSuccess = onSuccess; } @Override public void onSuccess(T result) { onSuccess.accept(result); } @Override public void onFailure(Throwable t) { } } }