/* * 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; import io.crate.concurrent.CompletableFutures; import javax.annotation.Nonnull; import java.util.Arrays; import java.util.Comparator; import java.util.concurrent.CompletionStage; /** * BatchIterator implementation that is backed by multiple other BatchIterators. * <p> * It will consume each iterator to it's end before consuming the next iterator in order to make sure that repeated * consumption via {@link #moveToStart()} always returns the same result. */ public class CompositeBatchIterator implements BatchIterator { private final BatchIterator[] iterators; private final CompositeColumns columns; private int idx = 0; /** * @param iterators underlying iterators to use; order of consumption may change if some of them are unloaded * to prefer loaded iterators over unloaded. */ public CompositeBatchIterator(BatchIterator... iterators) { assert iterators.length > 0 : "Must have at least 1 iterator"; // prefer loaded iterators over unloaded to improve performance in case only a subset of data is consumed Arrays.sort(iterators, Comparator.comparing(BatchIterator::allLoaded).reversed()); this.iterators = iterators; this.columns = new CompositeColumns(iterators); } @Override public Columns rowData() { return columns; } @Override public void moveToStart() { for (BatchIterator iterator : iterators) { iterator.moveToStart(); } resetIndex(); } private void resetIndex() { idx = 0; columns.updateInputs(idx); } @Override public boolean moveNext() { while (idx < iterators.length) { BatchIterator iterator = iterators[idx]; if (iterator.moveNext()) { return true; } if (iterator.allLoaded() == false) { return false; } idx++; columns.updateInputs(idx); } resetIndex(); return false; } @Override public void close() { for (BatchIterator iterator : iterators) { iterator.close(); } } @Override public CompletionStage<?> loadNextBatch() { for (BatchIterator iterator : iterators) { if (iterator.allLoaded()) { continue; } return iterator.loadNextBatch(); } return CompletableFutures.failedFuture(new IllegalStateException("BatchIterator already fully loaded")); } @Override public boolean allLoaded() { for (BatchIterator iterator : iterators) { if (iterator.allLoaded() == false) { return false; } } return true; } @Override public void kill(@Nonnull Throwable throwable) { for (BatchIterator iterator : iterators) { iterator.kill(throwable); } } }