/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate licenses this file * to you 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.data.join; import io.crate.data.BatchIterator; import io.crate.data.Columns; import javax.annotation.Nonnull; import java.util.concurrent.CompletionStage; import java.util.function.BooleanSupplier; import java.util.function.Function; /** * NestedLoop BatchIterator implementations * * - {@link #crossJoin(BatchIterator, BatchIterator)} * - {@link #leftJoin(BatchIterator, BatchIterator, Function)} * - {@link #rightJoin(BatchIterator, BatchIterator, Function)} * - {@link #fullOuterJoin(BatchIterator, BatchIterator, Function)} */ public class NestedLoopBatchIterator implements BatchIterator { /** * Create a BatchIterator that creates a full-outer-join of {@code left} and {@code right}. */ public static BatchIterator fullOuterJoin(BatchIterator left, BatchIterator right, Function<Columns, BooleanSupplier> joinCondition) { return new FullOuterJoinBatchIterator(left, right, joinCondition); } /** * Create a BatchIterator that creates a cross-join of {@code left} and {@code right}. */ public static BatchIterator crossJoin(BatchIterator left, BatchIterator right) { return new NestedLoopBatchIterator(left, right); } /** * Create a BatchIterator that creates the left-outer-join result of {@code left} and {@code right}. */ public static BatchIterator leftJoin(BatchIterator left, BatchIterator right, Function<Columns, BooleanSupplier> joinCondition) { return new LeftJoinBatchIterator(left, right, joinCondition); } /** * Create a BatchIterator that creates the right-outer-join result of {@code left} and {@code right}. */ public static BatchIterator rightJoin(BatchIterator left, BatchIterator right, Function<Columns, BooleanSupplier> joinCondition) { return new RightJoinBatchIterator(left, right, joinCondition); } final CombinedColumn rowData; final BatchIterator left; final BatchIterator right; /** * points to the batchIterator which will be used on the next {@link #moveNext()} call */ BatchIterator activeIt; NestedLoopBatchIterator(BatchIterator left, BatchIterator right) { this.left = left; this.right = right; this.activeIt = left; this.rowData = new CombinedColumn(left.rowData(), right.rowData()); } @Override public Columns rowData() { return rowData; } @Override public void moveToStart() { left.moveToStart(); right.moveToStart(); } @Override public boolean moveNext() { if (activeIt == left) { return tryAdvanceLeftAndRight(); } else { return tryAdvanceRight(); } } private boolean tryAdvanceRight() { if (right.moveNext()) { return true; } if (right.allLoaded() == false) { return false; } // new outer loop iteration right.moveToStart(); if (left.moveNext()) { return right.moveNext(); } activeIt = left; // left is either out of rows or needs to load more data - whatever the case, // next moveNext must operate on left return false; } private boolean tryAdvanceLeftAndRight() { while (left.moveNext()) { activeIt = right; if (right.moveNext()) { return true; } if (right.allLoaded() == false) { return false; } right.moveToStart(); } return false; } @Override public void close() { left.close(); right.close(); } @Override public CompletionStage<?> loadNextBatch() { return activeIt.loadNextBatch(); } @Override public boolean allLoaded() { return activeIt.allLoaded(); } @Override public void kill(@Nonnull Throwable throwable) { left.kill(throwable); right.kill(throwable); } }