/* * 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.Session; import com.facebook.presto.spi.block.Block; import com.facebook.presto.sql.gen.JoinFilterFunctionCompiler.JoinFilterFunctionFactory; import it.unimi.dsi.fastutil.ints.IntComparator; import it.unimi.dsi.fastutil.longs.LongArrayList; import java.util.List; import java.util.Optional; import java.util.function.Function; import static com.facebook.presto.SystemSessionProperties.isFastInequalityJoin; import static com.facebook.presto.operator.SyntheticAddress.decodePosition; import static com.facebook.presto.operator.SyntheticAddress.decodeSliceIndex; import static java.util.Objects.requireNonNull; public class JoinHashSupplier implements LookupSourceSupplier { private final Session session; private final PagesHash pagesHash; private final LongArrayList addresses; private final List<List<Block>> channels; private final Function<Optional<JoinFilterFunction>, PositionLinks> positionLinks; private final Optional<JoinFilterFunctionFactory> filterFunctionFactory; public JoinHashSupplier( Session session, PagesHashStrategy pagesHashStrategy, LongArrayList addresses, List<List<Block>> channels, Optional<JoinFilterFunctionFactory> filterFunctionFactory) { this.session = requireNonNull(session, "session is null"); this.addresses = requireNonNull(addresses, "addresses is null"); this.channels = requireNonNull(channels, "channels is null"); this.filterFunctionFactory = requireNonNull(filterFunctionFactory, "filterFunctionFactory is null"); requireNonNull(pagesHashStrategy, "pagesHashStrategy is null"); PositionLinks.Builder positionLinksBuilder; if (filterFunctionFactory.isPresent() && filterFunctionFactory.get().getSortChannel().isPresent() && isFastInequalityJoin(session)) { positionLinksBuilder = SortedPositionLinks.builder( addresses.size(), new PositionComparator(pagesHashStrategy, addresses)); } else { positionLinksBuilder = ArrayPositionLinks.builder(addresses.size()); } this.pagesHash = new PagesHash(addresses, pagesHashStrategy, positionLinksBuilder); this.positionLinks = positionLinksBuilder.build(); } @Override public long getHashCollisions() { return pagesHash.getHashCollisions(); } @Override public double getExpectedHashCollisions() { return pagesHash.getExpectedHashCollisions(); } @Override public JoinHash get() { // We need to create new JoinFilterFunction per each thread using it, since those functions // are not thread safe... Optional<JoinFilterFunction> filterFunction = filterFunctionFactory.map(factory -> factory.create(session.toConnectorSession(), addresses, channels)); return new JoinHash( pagesHash, filterFunction, positionLinks.apply(filterFunction)); } public static class PositionComparator implements IntComparator { private final PagesHashStrategy pagesHashStrategy; private final LongArrayList addresses; public PositionComparator(PagesHashStrategy pagesHashStrategy, LongArrayList addresses) { this.pagesHashStrategy = pagesHashStrategy; this.addresses = addresses; } @Override public int compare(int leftPosition, int rightPosition) { long leftPageAddress = addresses.getLong(leftPosition); int leftBlockIndex = decodeSliceIndex(leftPageAddress); int leftBlockPosition = decodePosition(leftPageAddress); long rightPageAddress = addresses.getLong(rightPosition); int rightBlockIndex = decodeSliceIndex(rightPageAddress); int rightBlockPosition = decodePosition(rightPageAddress); return pagesHashStrategy.compare(leftBlockIndex, leftBlockPosition, rightBlockIndex, rightBlockPosition); } @Override public int compare(Integer leftPosition, Integer rightPosition) { return compare(leftPosition.intValue(), rightPosition.intValue()); } } }