Skip to content

Commit

Permalink
Add predicates simplification to SimplifyExpressions
Browse files Browse the repository at this point in the history
It is possible that common sub-predicate occures in multiple predicates.
For instance:
 (X AND Y) OR (X AND V)
or
 (X OR Y) AND (X OR V)

In such cases it is possible to extract this common predicate:
 X AND (Y OR V)
or
 X OR (Y AND V)

This will let join predicates to be pushed to the top, therefore
allowing for faster joins.

Additionally, negations are pushed down to the bottom of expression tree.
  • Loading branch information
sopel39 authored and erichwang committed Feb 23, 2016
1 parent 15de73e commit 8692208
Show file tree
Hide file tree
Showing 9 changed files with 388 additions and 77 deletions.
1 change: 1 addition & 0 deletions presto-docs/src/main/sphinx/release.rst
Expand Up @@ -5,6 +5,7 @@ Release Notes
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1


release/release-0.140
release/release-0.139 release/release-0.139
release/release-0.138 release/release-0.138
release/release-0.137 release/release-0.137
Expand Down
8 changes: 8 additions & 0 deletions presto-docs/src/main/sphinx/release/release-0.140.rst
@@ -0,0 +1,8 @@
=============
Release 0.139
=============

General Changes
---------------

* Optimize predicate expressions to minimize redundancies.
116 changes: 48 additions & 68 deletions presto-main/src/main/java/com/facebook/presto/sql/ExpressionUtils.java
Expand Up @@ -37,16 +37,12 @@


import static com.facebook.presto.sql.tree.BooleanLiteral.FALSE_LITERAL; 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.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.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.facebook.presto.util.ImmutableCollectors.toImmutableList;
import static com.google.common.base.Predicates.not; import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Iterables.contains;
import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.newArrayList;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;


Expand All @@ -56,24 +52,26 @@ private ExpressionUtils() {}


public static List<Expression> extractConjuncts(Expression expression) public static List<Expression> extractConjuncts(Expression expression)
{ {
if (expression instanceof LogicalBinaryExpression && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.AND) { return extractPredicates(LogicalBinaryExpression.Type.AND, expression);
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) public static List<Expression> extractDisjuncts(Expression expression)
{ {
if (expression instanceof LogicalBinaryExpression && ((LogicalBinaryExpression) expression).getType() == LogicalBinaryExpression.Type.OR) { return extractPredicates(LogicalBinaryExpression.Type.OR, expression);
LogicalBinaryExpression or = (LogicalBinaryExpression) expression; }

public static List<Expression> extractPredicates(LogicalBinaryExpression expression)
{
return extractPredicates(expression.getType(), expression);
}

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


Expand Down Expand Up @@ -114,6 +112,20 @@ public static Expression binaryExpression(LogicalBinaryExpression.Type type, Ite
return queue.remove(); return queue.remove();
} }


public static Expression combinePredicates(LogicalBinaryExpression.Type type, Expression... expressions)
{
return combinePredicates(type, Arrays.asList(expressions));
}

public static Expression combinePredicates(LogicalBinaryExpression.Type type, Iterable<Expression> expressions)
{
if (type == LogicalBinaryExpression.Type.AND) {
return combineConjuncts(expressions);
}

return combineDisjuncts(expressions);
}

public static Expression combineConjuncts(Expression... expressions) public static Expression combineConjuncts(Expression... expressions)
{ {
return combineConjuncts(Arrays.asList(expressions)); return combineConjuncts(Arrays.asList(expressions));
Expand All @@ -129,11 +141,16 @@ public static Expression combineConjunctsWithDefault(Iterable<Expression> expres
requireNonNull(expressions, "expressions is null"); requireNonNull(expressions, "expressions is null");


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


// Strip out all true literal conjuncts // Strip out all true literal conjuncts
expressions = Iterables.filter(expressions, not(Predicates.<Expression>equalTo(TRUE_LITERAL))); expressions = filter(expressions, not(Predicates.<Expression>equalTo(TRUE_LITERAL)));
expressions = removeDuplicates(expressions); expressions = removeDuplicates(expressions);

if (contains(expressions, FALSE_LITERAL)) {
return FALSE_LITERAL;
}

return Iterables.isEmpty(expressions) ? emptyDefault : and(expressions); return Iterables.isEmpty(expressions) ? emptyDefault : and(expressions);
} }


Expand All @@ -152,11 +169,16 @@ public static Expression combineDisjunctsWithDefault(Iterable<Expression> expres
requireNonNull(expressions, "expressions is null"); requireNonNull(expressions, "expressions is null");


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


// Strip out all false literal disjuncts // Strip out all false literal disjuncts
expressions = Iterables.filter(expressions, not(Predicates.<Expression>equalTo(FALSE_LITERAL))); expressions = filter(expressions, not(Predicates.<Expression>equalTo(FALSE_LITERAL)));
expressions = removeDuplicates(expressions); expressions = removeDuplicates(expressions);

if (contains(expressions, TRUE_LITERAL)) {
return TRUE_LITERAL;
}

return Iterables.isEmpty(expressions) ? emptyDefault : or(expressions); return Iterables.isEmpty(expressions) ? emptyDefault : or(expressions);
} }


Expand All @@ -173,28 +195,6 @@ public static Expression stripDeterministicConjuncts(Expression expression)
.collect(toImmutableList())); .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) public static Function<Expression, Expression> expressionOrNullSymbols(final Predicate<Symbol>... nullSymbolScopes)
{ {
return expression -> { return expression -> {
Expand Down Expand Up @@ -222,41 +222,21 @@ public static Function<Expression, Expression> expressionOrNullSymbols(final Pre
private static Iterable<Expression> removeDuplicates(Iterable<Expression> expressions) private static Iterable<Expression> removeDuplicates(Iterable<Expression> expressions)
{ {
// Capture all non-deterministic predicates // Capture all non-deterministic predicates
Iterable<Expression> nonDeterministicDisjuncts = Iterables.filter(expressions, not(DeterminismEvaluator::isDeterministic)); Iterable<Expression> nonDeterministicDisjuncts = filter(expressions, not(DeterminismEvaluator::isDeterministic));


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


return Iterables.concat(nonDeterministicDisjuncts, deterministicDisjuncts); 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) public static Expression normalize(Expression expression)
{ {
if (expression instanceof NotExpression) { if (expression instanceof NotExpression) {
NotExpression not = (NotExpression) expression; NotExpression not = (NotExpression) expression;
if (not.getValue() instanceof ComparisonExpression && ((ComparisonExpression) not.getValue()).getType() != IS_DISTINCT_FROM) { if (not.getValue() instanceof ComparisonExpression && ((ComparisonExpression) not.getValue()).getType() != IS_DISTINCT_FROM) {
ComparisonExpression comparison = (ComparisonExpression) not.getValue(); ComparisonExpression comparison = (ComparisonExpression) not.getValue();
return new ComparisonExpression(negate(comparison.getType()), comparison.getLeft(), comparison.getRight()); return new ComparisonExpression(comparison.getType().negate(), comparison.getLeft(), comparison.getRight());
} }
} }
return expression; return expression;
Expand Down
Expand Up @@ -59,7 +59,6 @@
import static com.facebook.presto.sql.ExpressionUtils.and; import static com.facebook.presto.sql.ExpressionUtils.and;
import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts;
import static com.facebook.presto.sql.ExpressionUtils.combineDisjunctsWithDefault; import static com.facebook.presto.sql.ExpressionUtils.combineDisjunctsWithDefault;
import static com.facebook.presto.sql.ExpressionUtils.flipComparison;
import static com.facebook.presto.sql.ExpressionUtils.or; import static com.facebook.presto.sql.ExpressionUtils.or;
import static com.facebook.presto.sql.planner.LiteralInterpreter.toExpression; import static com.facebook.presto.sql.planner.LiteralInterpreter.toExpression;
import static com.facebook.presto.sql.tree.BooleanLiteral.FALSE_LITERAL; import static com.facebook.presto.sql.tree.BooleanLiteral.FALSE_LITERAL;
Expand Down Expand Up @@ -545,7 +544,7 @@ private static Optional<NormalizedSimpleComparison> toNormalizedSimpleComparison
return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) left, comparison.getType(), new NullableValue(expressionTypes.get(comparison.getRight()), right))); return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) left, comparison.getType(), new NullableValue(expressionTypes.get(comparison.getRight()), right)));
} }
if (right instanceof QualifiedNameReference && !(left instanceof Expression)) { if (right instanceof QualifiedNameReference && !(left instanceof Expression)) {
return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) right, flipComparison(comparison.getType()), new NullableValue(expressionTypes.get(comparison.getLeft()), left))); return Optional.of(new NormalizedSimpleComparison((QualifiedNameReference) right, comparison.getType().flip(), new NullableValue(expressionTypes.get(comparison.getLeft()), left)));
} }
return Optional.empty(); return Optional.empty();
} }
Expand Down
Expand Up @@ -79,7 +79,6 @@


import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.sql.ExpressionUtils.flipComparison;
import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED;
import static com.facebook.presto.sql.planner.ExpressionInterpreter.evaluateConstantExpression; import static com.facebook.presto.sql.planner.ExpressionInterpreter.evaluateConstantExpression;
import static com.facebook.presto.sql.tree.ComparisonExpression.Type.EQUAL; import static com.facebook.presto.sql.tree.ComparisonExpression.Type.EQUAL;
Expand Down Expand Up @@ -258,7 +257,7 @@ protected RelationPlan visitJoin(Join node, Void context)
else if (firstDependencies.stream().allMatch(right.canResolvePredicate()) && secondDependencies.stream().allMatch(left.canResolvePredicate())) { else if (firstDependencies.stream().allMatch(right.canResolvePredicate()) && secondDependencies.stream().allMatch(left.canResolvePredicate())) {
leftExpression = comparison.getRight(); leftExpression = comparison.getRight();
rightExpression = comparison.getLeft(); rightExpression = comparison.getLeft();
comparisonType = flipComparison(comparisonType); comparisonType = comparisonType.flip();
} }
else { else {
// must have a complex expression that involves both tuples on one side of the comparison expression (e.g., coalesce(left.x, right.x) = 1) // must have a complex expression that involves both tuples on one side of the comparison expression (e.g., coalesce(left.x, right.x) = 1)
Expand Down

0 comments on commit 8692208

Please sign in to comment.