/*
* 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.sql.planner.optimizations;
import com.facebook.presto.spi.ConstantProperty;
import com.facebook.presto.spi.GroupingProperty;
import com.facebook.presto.spi.LocalProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.PeekingIterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.google.common.collect.Iterators.peekingIterator;
final class LocalProperties
{
private LocalProperties()
{
}
public static <T> List<LocalProperty<T>> none()
{
return ImmutableList.of();
}
public static <T> List<LocalProperty<T>> grouped(Collection<T> columns)
{
return ImmutableList.of(new GroupingProperty<>(columns));
}
public static <T> List<LocalProperty<T>> stripLeadingConstants(List<? extends LocalProperty<T>> properties)
{
PeekingIterator<? extends LocalProperty<T>> iterator = peekingIterator(properties.iterator());
while (iterator.hasNext() && iterator.peek() instanceof ConstantProperty) {
iterator.next();
}
return ImmutableList.copyOf(iterator);
}
public static <T> Set<T> extractLeadingConstants(List<? extends LocalProperty<T>> properties)
{
ImmutableSet.Builder<T> builder = ImmutableSet.builder();
PeekingIterator<? extends LocalProperty<T>> iterator = peekingIterator(properties.iterator());
while (iterator.hasNext() && iterator.peek() instanceof ConstantProperty) {
builder.add(((ConstantProperty<T>) iterator.next()).getColumn());
}
return builder.build();
}
/**
* Translates the properties as much as possible, and truncates at the first non-translatable property
*/
public static <X, Y> List<LocalProperty<Y>> translate(List<? extends LocalProperty<X>> properties, Function<X, Optional<Y>> translator)
{
properties = normalizeAndPrune(properties);
ImmutableList.Builder<LocalProperty<Y>> builder = ImmutableList.builder();
for (LocalProperty<X> property : properties) {
Optional<LocalProperty<Y>> translated = property.translate(translator);
if (translated.isPresent()) {
builder.add(translated.get());
}
else if (!(property instanceof ConstantProperty)) {
break; // Only break if we fail to translate non-constants
}
}
return builder.build();
}
/**
* Attempt to match the desired properties to a sequence of known properties.
* <p>
* Returns a list of the same length as the original. Entries are:
* - Optional.empty(): the property was satisfied completely
* - non-empty: the (simplified) property that was not satisfied
*/
public static <T> List<Optional<LocalProperty<T>>> match(List<LocalProperty<T>> actuals, List<LocalProperty<T>> desired)
{
// After normalizing actuals, each symbol should only appear once
PeekingIterator<LocalProperty<T>> actualIterator = peekingIterator(normalizeAndPrune(actuals).iterator());
Set<T> constants = new HashSet<>();
boolean consumeMoreActuals = true;
List<Optional<LocalProperty<T>>> result = new ArrayList<>(desired.size());
for (LocalProperty<T> desiredProperty : desired) {
while (consumeMoreActuals && actualIterator.hasNext() && desiredProperty.isSimplifiedBy(actualIterator.peek())) {
constants.addAll(actualIterator.next().getColumns());
}
Optional<LocalProperty<T>> simplifiedDesired = desiredProperty.withConstants(constants);
consumeMoreActuals &= !simplifiedDesired.isPresent(); // Only continue processing actuals if all previous desired properties were fully satisfied
result.add(simplifiedDesired);
}
return result;
}
/**
* Normalizes the local properties and potentially consolidates it to the smallest possible list
* NOTE: When normalized, each symbol will only appear once
*/
public static <T> List<LocalProperty<T>> normalizeAndPrune(List<? extends LocalProperty<T>> localProperties)
{
return normalize(localProperties).stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
/**
* Normalizes the local properties by removing redundant symbols, but retains the original local property positions
*/
public static <T> List<Optional<LocalProperty<T>>> normalize(List<? extends LocalProperty<T>> localProperties)
{
List<Optional<LocalProperty<T>>> normalizedProperties = new ArrayList<>(localProperties.size());
Set<T> constants = new HashSet<>();
for (LocalProperty<T> localProperty : localProperties) {
normalizedProperties.add(localProperty.withConstants(constants));
constants.addAll(localProperty.getColumns());
}
return normalizedProperties;
}
}