com.facebook.presto.sql.ExpressionUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.presto.sql.ExpressionUtils.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;

import com.facebook.presto.sql.planner.DependencyExtractor;
import com.facebook.presto.sql.planner.DeterminismEvaluator;
import com.facebook.presto.sql.planner.Symbol;
import com.facebook.presto.sql.tree.ComparisonExpression;
import com.facebook.presto.sql.tree.Expression;
import com.facebook.presto.sql.tree.IsNullPredicate;
import com.facebook.presto.sql.tree.LogicalBinaryExpression;
import com.facebook.presto.sql.tree.NotExpression;
import com.facebook.presto.sql.tree.QualifiedNameReference;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;

import static com.facebook.presto.sql.tree.BooleanLiteral.FALSE_LITERAL;
import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.EQUAL;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.GREATER_THAN;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.GREATER_THAN_OR_EQUAL;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.IS_DISTINCT_FROM;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.LESS_THAN;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.LESS_THAN_OR_EQUAL;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.NOT_EQUAL;
import static com.facebook.presto.util.ImmutableCollectors.toImmutableList;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Objects.requireNonNull;

public final class ExpressionUtils {
    private ExpressionUtils() {
    }

    public static List<Expression> extractConjuncts(Expression expression) {
        if (expression instanceof LogicalBinaryExpression
                && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.AND) {
            LogicalBinaryExpression and = (LogicalBinaryExpression) expression;
            return ImmutableList.<Expression>builder().addAll(extractConjuncts(and.getLeft()))
                    .addAll(extractConjuncts(and.getRight())).build();
        }

        return ImmutableList.of(expression);
    }

    public static List<Expression> extractDisjuncts(Expression expression) {
        if (expression instanceof LogicalBinaryExpression
                && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.OR) {
            LogicalBinaryExpression or = (LogicalBinaryExpression) expression;
            return ImmutableList.<Expression>builder().addAll(extractDisjuncts(or.getLeft()))
                    .addAll(extractDisjuncts(or.getRight())).build();
        }

        return ImmutableList.of(expression);
    }

    public static Expression and(Expression... expressions) {
        return and(Arrays.asList(expressions));
    }

    public static Expression and(Iterable<Expression> expressions) {
        return binaryExpression(LogicalBinaryExpression.Type.AND, expressions);
    }

    public static Expression or(Expression... expressions) {
        return or(Arrays.asList(expressions));
    }

    public static Expression or(Iterable<Expression> expressions) {
        return binaryExpression(LogicalBinaryExpression.Type.OR, expressions);
    }

    public static Expression binaryExpression(LogicalBinaryExpression.Type type, Iterable<Expression> expressions) {
        requireNonNull(type, "type is null");
        requireNonNull(expressions, "expressions is null");
        Preconditions.checkArgument(!Iterables.isEmpty(expressions), "expressions is empty");

        // build balanced tree for efficient recursive processing
        Queue<Expression> queue = new ArrayDeque<>(newArrayList(expressions));
        while (queue.size() > 1) {
            queue.add(new LogicalBinaryExpression(type, queue.remove(), queue.remove()));
        }
        return queue.remove();
    }

    public static Expression combineConjuncts(Expression... expressions) {
        return combineConjuncts(Arrays.asList(expressions));
    }

    public static Expression combineConjuncts(Iterable<Expression> expressions) {
        return combineConjunctsWithDefault(expressions, TRUE_LITERAL);
    }

    public static Expression combineConjunctsWithDefault(Iterable<Expression> expressions,
            Expression emptyDefault) {
        requireNonNull(expressions, "expressions is null");

        // Flatten all the expressions into their component conjuncts
        expressions = Iterables.concat(Iterables.transform(expressions, ExpressionUtils::extractConjuncts));

        // Strip out all true literal conjuncts
        expressions = Iterables.filter(expressions, not(Predicates.<Expression>equalTo(TRUE_LITERAL)));
        expressions = removeDuplicates(expressions);
        return Iterables.isEmpty(expressions) ? emptyDefault : and(expressions);
    }

    public static Expression combineDisjuncts(Expression... expressions) {
        return combineDisjuncts(Arrays.asList(expressions));
    }

    public static Expression combineDisjuncts(Iterable<Expression> expressions) {
        return combineDisjunctsWithDefault(expressions, FALSE_LITERAL);
    }

    public static Expression combineDisjunctsWithDefault(Iterable<Expression> expressions,
            Expression emptyDefault) {
        requireNonNull(expressions, "expressions is null");

        // Flatten all the expressions into their component disjuncts
        expressions = Iterables.concat(Iterables.transform(expressions, ExpressionUtils::extractDisjuncts));

        // Strip out all false literal disjuncts
        expressions = Iterables.filter(expressions, not(Predicates.<Expression>equalTo(FALSE_LITERAL)));
        expressions = removeDuplicates(expressions);
        return Iterables.isEmpty(expressions) ? emptyDefault : or(expressions);
    }

    public static Expression stripNonDeterministicConjuncts(Expression expression) {
        return combineConjuncts(filter(extractConjuncts(expression), DeterminismEvaluator::isDeterministic));
    }

    public static Expression stripDeterministicConjuncts(Expression expression) {
        return combineConjuncts(extractConjuncts(expression).stream()
                .filter((conjunct) -> !DeterminismEvaluator.isDeterministic(conjunct)).collect(toImmutableList()));
    }

    public static ComparisonExpression.Type flipComparison(ComparisonExpression.Type type) {
        switch (type) {
        case EQUAL:
            return EQUAL;
        case NOT_EQUAL:
            return NOT_EQUAL;
        case LESS_THAN:
            return GREATER_THAN;
        case LESS_THAN_OR_EQUAL:
            return GREATER_THAN_OR_EQUAL;
        case GREATER_THAN:
            return LESS_THAN;
        case GREATER_THAN_OR_EQUAL:
            return LESS_THAN_OR_EQUAL;
        case IS_DISTINCT_FROM:
            return IS_DISTINCT_FROM;
        default:
            throw new IllegalArgumentException("Unsupported comparison: " + type);
        }
    }

    public static Function<Expression, Expression> expressionOrNullSymbols(
            final Predicate<Symbol>... nullSymbolScopes) {
        return expression -> {
            ImmutableList.Builder<Expression> resultDisjunct = ImmutableList.builder();
            resultDisjunct.add(expression);

            for (Predicate<Symbol> nullSymbolScope : nullSymbolScopes) {
                Iterable<Symbol> symbols = filter(DependencyExtractor.extractUnique(expression), nullSymbolScope);
                if (Iterables.isEmpty(symbols)) {
                    continue;
                }

                ImmutableList.Builder<Expression> nullConjuncts = ImmutableList.builder();
                for (Symbol symbol : symbols) {
                    nullConjuncts.add(new IsNullPredicate(new QualifiedNameReference(symbol.toQualifiedName())));
                }

                resultDisjunct.add(and(nullConjuncts.build()));
            }

            return or(resultDisjunct.build());
        };
    }

    private static Iterable<Expression> removeDuplicates(Iterable<Expression> expressions) {
        // Capture all non-deterministic predicates
        Iterable<Expression> nonDeterministicDisjuncts = Iterables.filter(expressions,
                not(DeterminismEvaluator::isDeterministic));

        // Capture and de-dupe all deterministic predicates
        Iterable<Expression> deterministicDisjuncts = ImmutableSet
                .copyOf(Iterables.filter(expressions, DeterminismEvaluator::isDeterministic));

        return Iterables.concat(nonDeterministicDisjuncts, deterministicDisjuncts);
    }

    private static ComparisonExpression.Type negate(ComparisonExpression.Type type) {
        switch (type) {
        case EQUAL:
            return NOT_EQUAL;
        case NOT_EQUAL:
            return EQUAL;
        case LESS_THAN:
            return GREATER_THAN_OR_EQUAL;
        case LESS_THAN_OR_EQUAL:
            return GREATER_THAN;
        case GREATER_THAN:
            return LESS_THAN_OR_EQUAL;
        case GREATER_THAN_OR_EQUAL:
            return LESS_THAN;
        default:
            throw new IllegalArgumentException("Unsupported comparison: " + type);
        }
    }

    public static Expression normalize(Expression expression) {
        if (expression instanceof NotExpression) {
            NotExpression not = (NotExpression) expression;
            if (not.getValue() instanceof ComparisonExpression) {
                ComparisonExpression comparison = (ComparisonExpression) not.getValue();
                return new ComparisonExpression(negate(comparison.getType()), comparison.getLeft(),
                        comparison.getRight());
            }
        }
        return expression;
    }
}