com.facebook.presto.sql.planner.optimizations.PreferredProperties.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.presto.sql.planner.optimizations.PreferredProperties.java

Source

/*
 * 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.LocalProperty;
import com.facebook.presto.sql.planner.Symbol;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

import javax.annotation.concurrent.Immutable;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import static com.facebook.presto.util.ImmutableCollectors.toImmutableSet;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;

class PreferredProperties {
    private final Optional<Global> globalProperties;
    private final List<LocalProperty<Symbol>> localProperties;

    private PreferredProperties(Optional<Global> globalProperties,
            List<? extends LocalProperty<Symbol>> localProperties) {
        requireNonNull(globalProperties, "globalProperties is null");
        requireNonNull(localProperties, "localProperties is null");

        this.globalProperties = globalProperties;
        this.localProperties = ImmutableList.copyOf(localProperties);
    }

    public static PreferredProperties any() {
        return builder().build();
    }

    public static PreferredProperties undistributed() {
        return builder().global(Global.undistributed()).build();
    }

    public static PreferredProperties partitioned(Set<Symbol> columns) {
        return builder().global(Global.distributed(Partitioning.partitioned(columns))).build();
    }

    public static PreferredProperties distributed() {
        return builder().global(Global.distributed()).build();
    }

    public static PreferredProperties hashPartitioned(List<Symbol> columns) {
        return builder().global(Global.distributed(Partitioning.hashPartitioned(columns))).build();
    }

    public static PreferredProperties hashPartitionedWithLocal(List<Symbol> columns,
            List<? extends LocalProperty<Symbol>> localProperties) {
        return builder().global(Global.distributed(Partitioning.hashPartitioned(columns))).local(localProperties)
                .build();
    }

    public static PreferredProperties partitionedWithLocal(Set<Symbol> columns,
            List<? extends LocalProperty<Symbol>> localProperties) {
        return builder().global(Global.distributed(Partitioning.partitioned(columns))).local(localProperties)
                .build();
    }

    public static PreferredProperties undistributedWithLocal(
            List<? extends LocalProperty<Symbol>> localProperties) {
        return builder().global(Global.undistributed()).local(localProperties).build();
    }

    public static PreferredProperties local(List<? extends LocalProperty<Symbol>> localProperties) {
        return builder().local(localProperties).build();
    }

    public Optional<Global> getGlobalProperties() {
        return globalProperties;
    }

    public List<LocalProperty<Symbol>> getLocalProperties() {
        return localProperties;
    }

    public PreferredProperties translate(Function<Symbol, Optional<Symbol>> translator) {
        Optional<Global> newGlobalProperties = globalProperties.map(global -> global.translate(translator));
        List<LocalProperty<Symbol>> newLocalProperties = LocalProperties.translate(localProperties, translator);
        return new PreferredProperties(newGlobalProperties, newLocalProperties);
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private Optional<Global> globalProperties = Optional.empty();
        private List<LocalProperty<Symbol>> localProperties = ImmutableList.of();

        public Builder global(Global globalProperties) {
            this.globalProperties = Optional.of(globalProperties);
            return this;
        }

        public Builder global(PreferredProperties other) {
            this.globalProperties = other.globalProperties;
            return this;
        }

        public Builder local(List<? extends LocalProperty<Symbol>> localProperties) {
            this.localProperties = ImmutableList.copyOf(localProperties);
            return this;
        }

        public Builder local(PreferredProperties other) {
            this.localProperties = ImmutableList.copyOf(other.localProperties);
            return this;
        }

        public PreferredProperties build() {
            return new PreferredProperties(globalProperties, localProperties);
        }
    }

    public static PreferredProperties derivePreferences(PreferredProperties parentProperties,
            Set<Symbol> partitioningColumns, List<LocalProperty<Symbol>> localProperties) {
        return derivePreferences(parentProperties, partitioningColumns, Optional.empty(), localProperties);
    }

    /**
     * Derive current node's preferred properties based on parent's preferences
     *
     * @param parentProperties Parent's preferences (translated)
     * @param partitioningColumns partitioning columns of current node
     * @param hashingColumns hashing columns of current node
     * @param localProperties local properties of current node
     * @return PreferredProperties for current node
     */
    public static PreferredProperties derivePreferences(PreferredProperties parentProperties,
            Set<Symbol> partitioningColumns, Optional<List<Symbol>> hashingColumns,
            List<LocalProperty<Symbol>> localProperties) {
        if (hashingColumns.isPresent()) {
            checkState(partitioningColumns.equals(ImmutableSet.copyOf(hashingColumns.get())),
                    "hashingColumns and partitioningColumns must be the same");
        }

        List<LocalProperty<Symbol>> local = ImmutableList.<LocalProperty<Symbol>>builder().addAll(localProperties)
                .addAll(parentProperties.getLocalProperties()).build();

        // Check we need to be hash partitioned
        if (hashingColumns.isPresent()) {
            return hashPartitionedWithLocal(hashingColumns.get(), local);
        }

        if (parentProperties.getGlobalProperties().isPresent()) {
            Global global = parentProperties.getGlobalProperties().get();
            if (global.getPartitioningProperties().isPresent()) {
                Partitioning partitioning = global.getPartitioningProperties().get();

                // If parent's hash partitioning satisfies our partitioning, use parent's hash partitioning
                if (partitioning.getHashingOrder().isPresent()
                        && partitioningColumns.equals(ImmutableSet.copyOf(partitioning.getHashingOrder().get()))) {
                    List<Symbol> hashingSymbols = partitioning.getHashingOrder().get();
                    return hashPartitionedWithLocal(hashingSymbols, local);
                }

                // if the child plan is partitioned by the common columns between our requirements and our parent's, it can satisfy both in one shot
                Set<Symbol> parentPartitioningColumns = partitioning.getPartitioningColumns();
                Set<Symbol> common = Sets.intersection(partitioningColumns, parentPartitioningColumns);

                // If we find common partitioning columns, use them, else use child's partitioning columns
                if (!common.isEmpty()) {
                    return partitionedWithLocal(common, local);
                }
                return partitionedWithLocal(partitioningColumns, local);
            }
        }
        return partitionedWithLocal(partitioningColumns, local);
    }

    @Immutable
    public static final class Global {
        private final boolean distributed;
        private final Optional<Partitioning> partitioningProperties; // if missing => partitioned with some unknown scheme

        private Global(boolean distributed, Optional<Partitioning> partitioningProperties) {
            this.distributed = distributed;
            this.partitioningProperties = Objects.requireNonNull(partitioningProperties,
                    "partitioningProperties is null");
        }

        public static Global undistributed() {
            return new Global(false, Optional.of(Partitioning.singlePartition()));
        }

        public static Global distributed(Optional<Partitioning> partitioningProperties) {
            return new Global(true, partitioningProperties);
        }

        public static Global distributed() {
            return distributed(Optional.<Partitioning>empty());
        }

        public static Global distributed(Partitioning partitioning) {
            return distributed(Optional.of(partitioning));
        }

        public boolean isDistributed() {
            return distributed;
        }

        public Optional<Partitioning> getPartitioningProperties() {
            return partitioningProperties;
        }

        public boolean isHashPartitioned() {
            return partitioningProperties.isPresent() && partitioningProperties.get().getHashingOrder().isPresent();
        }

        public Global translate(Function<Symbol, Optional<Symbol>> translator) {
            if (!isDistributed()) {
                return this;
            }
            return distributed(partitioningProperties.flatMap(properties -> properties.translate(translator)));
        }

        @Override
        public int hashCode() {
            return Objects.hash(distributed, partitioningProperties);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
            final Global other = (Global) obj;
            return Objects.equals(this.distributed, other.distributed)
                    && Objects.equals(this.partitioningProperties, other.partitioningProperties);
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("distributed", distributed)
                    .add("partitioningProperties", partitioningProperties).toString();
        }
    }

    @Immutable
    public static final class Partitioning {
        private final Set<Symbol> partitioningColumns;
        private final Optional<List<Symbol>> hashingOrder; // If populated, this list will contain the same symbols as partitioningColumns

        private Partitioning(Set<Symbol> partitioningColumns, Optional<List<Symbol>> hashingOrder) {
            this.partitioningColumns = ImmutableSet
                    .copyOf(Objects.requireNonNull(partitioningColumns, "partitioningColumns is null"));
            this.hashingOrder = Objects.requireNonNull(hashingOrder, "hashingOrder is null")
                    .map(ImmutableList::copyOf);
        }

        public static Partitioning hashPartitioned(List<Symbol> columns) {
            return new Partitioning(ImmutableSet.copyOf(columns), Optional.of(columns));
        }

        public static Partitioning partitioned(Set<Symbol> columns) {
            return new Partitioning(columns, Optional.<List<Symbol>>empty());
        }

        public static Partitioning singlePartition() {
            return partitioned(ImmutableSet.of());
        }

        public Set<Symbol> getPartitioningColumns() {
            return partitioningColumns;
        }

        public Optional<List<Symbol>> getHashingOrder() {
            return hashingOrder;
        }

        public Optional<Partitioning> translate(Function<Symbol, Optional<Symbol>> translator) {
            Set<Symbol> newPartitioningColumns = partitioningColumns.stream().map(translator)
                    .filter(Optional::isPresent).map(Optional::get).collect(toImmutableSet());

            // If nothing can be translated, then we won't have any partitioning preferences
            if (newPartitioningColumns.isEmpty()) {
                return Optional.empty();
            }

            Optional<List<Symbol>> newHashingOrder = hashingOrder.flatMap(columns -> {
                ImmutableList.Builder<Symbol> builder = ImmutableList.builder();
                for (Symbol column : columns) {
                    Optional<Symbol> translated = translator.apply(column);
                    if (!translated.isPresent()) {
                        return Optional.empty();
                    }
                    builder.add(translated.get());
                }
                return Optional.of(builder.build());
            });

            return Optional.of(new Partitioning(newPartitioningColumns, newHashingOrder));
        }

        @Override
        public int hashCode() {
            return Objects.hash(partitioningColumns, hashingOrder);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null || getClass() != obj.getClass()) {
                return false;
            }
            final Partitioning other = (Partitioning) obj;
            return Objects.equals(this.partitioningColumns, other.partitioningColumns)
                    && Objects.equals(this.hashingOrder, other.hashingOrder);
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("partitioningColumns", partitioningColumns)
                    .add("hashingOrder", hashingOrder).toString();
        }
    }
}