From 8b1e53fde4421b5ff50b42738fbf167b24374110 Mon Sep 17 00:00:00 2001 From: praveenkrishna Date: Wed, 23 Jan 2019 15:26:17 +0530 Subject: [PATCH 1/2] Simplify coalesce expressions For coalesce expression like coalesce(colA, colA) will be simplified to colA and for expressions like coalesce('1', colA) will be simplified to '1'. --- .../sql/planner/ExpressionInterpreter.java | 26 +++++++++++++++---- .../sql/TestExpressionInterpreter.java | 10 ++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java index 3caf5cf2a989..581a98a006c3 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java @@ -102,6 +102,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.util.ArrayList; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -118,6 +119,7 @@ import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Iterables.getOnlyElement; import static io.prestosql.SystemSessionProperties.isLegacyRowFieldOrdinalAccessEnabled; import static io.prestosql.operator.scalar.ScalarFunctionImplementation.NullConvention.RETURN_NULL_ON_NULL; import static io.prestosql.spi.StandardErrorCode.NOT_SUPPORTED; @@ -128,6 +130,7 @@ import static io.prestosql.sql.analyzer.ExpressionAnalyzer.createConstantAnalyzer; import static io.prestosql.sql.analyzer.TypeSignatureProvider.fromTypes; import static io.prestosql.sql.gen.VarArgsToMapAdapterGenerator.generateVarArgsToMapAdapter; +import static io.prestosql.sql.planner.DeterminismEvaluator.isDeterministic; import static io.prestosql.sql.planner.iterative.rule.CanonicalizeExpressionRewriter.canonicalizeExpression; import static io.prestosql.type.LikeFunctions.isLikePattern; import static io.prestosql.type.LikeFunctions.unescapeLiteralLikePattern; @@ -551,14 +554,27 @@ protected Object visitCoalesceExpression(CoalesceExpression node, Object context if ((!values.isEmpty() && !(values.get(0) instanceof Expression)) || values.size() == 1) { return values.get(0); } - - List expressions = values.stream() - .map(value -> toExpression(value, type)) - .collect(Collectors.toList()); + ImmutableList.Builder operandsBuilder = ImmutableList.builder(); + Set visitedExpression = new HashSet<>(); + for (Object value : values) { + Expression expression = toExpression(value, type); + if (!isDeterministic(expression) || visitedExpression.add(expression)) { + operandsBuilder.add(expression); + } + // TODO: Replace this logic with an anlyzer which specifies whether it evaluates to null + if (expression instanceof Literal && !(expression instanceof NullLiteral)) { + break; + } + } + List expressions = operandsBuilder.build(); if (expressions.isEmpty()) { return null; } + + if (expressions.size() == 1) { + return getOnlyElement(expressions); + } return new CoalesceExpression(expressions); } @@ -642,7 +658,7 @@ else if (!found && result) { .filter(DeterminismEvaluator::isDeterministic) .distinct(), expressionValues.stream() - .filter((expression -> !DeterminismEvaluator.isDeterministic(expression)))) + .filter((expression -> !isDeterministic(expression)))) .collect(toImmutableList()); return new InPredicate(toExpression(value, type), new InListExpression(simplifiedExpressionValues)); } diff --git a/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java b/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java index 55127828433b..364c613dc144 100644 --- a/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java +++ b/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java @@ -102,6 +102,7 @@ public class TestExpressionInterpreter .put(new Symbol("unbound_integer"), INTEGER) .put(new Symbol("unbound_long"), BIGINT) .put(new Symbol("unbound_long2"), BIGINT) + .put(new Symbol("unbound_long3"), BIGINT) .put(new Symbol("unbound_string"), VARCHAR) .put(new Symbol("unbound_double"), DOUBLE) .put(new Symbol("unbound_boolean"), BOOLEAN) @@ -1136,7 +1137,14 @@ public void testCoalesce() assertOptimizedEquals("coalesce(2 * 3 * unbound_integer, 1.0E0/2.0E0, null)", "coalesce(6 * unbound_integer, 0.5E0)"); assertOptimizedEquals("coalesce(unbound_integer, 2, 1.0E0/2.0E0, 12.34E0, null)", "coalesce(unbound_integer, 2.0E0, 0.5E0, 12.34E0)"); assertOptimizedMatches("coalesce(0 / 0 > 1, unbound_boolean, 0 / 0 = 0)", - "coalesce(cast(fail() as boolean), unbound_boolean, cast(fail() as boolean))"); + "coalesce(cast(fail() as boolean), unbound_boolean)"); + assertOptimizedMatches("coalesce(unbound_long, unbound_long)", "unbound_long"); + assertOptimizedMatches("coalesce(2 * unbound_long, 2 * unbound_long)", "BIGINT '2' * unbound_long"); + assertOptimizedMatches("coalesce(unbound_long, unbound_long2, unbound_long)", "coalesce(unbound_long, unbound_long2)"); + assertOptimizedMatches("coalesce(unbound_long, unbound_long2, unbound_long, unbound_long3)", "coalesce(unbound_long, unbound_long2, unbound_long3)"); + assertOptimizedEquals("coalesce(6, unbound_long2, unbound_long, unbound_long3)", "6"); + assertOptimizedEquals("coalesce(2 * 3, unbound_long2, unbound_long, unbound_long3)", "6"); + assertOptimizedMatches("coalesce(random(), random(), 5)", "coalesce(random(), random(), 5E0)"); } @Test From c51fa6b108d25f87fb38055607d62a39ee545224 Mon Sep 17 00:00:00 2001 From: praveenkrishna Date: Thu, 24 Jan 2019 19:28:51 +0530 Subject: [PATCH 2/2] Flatten coalesce expressions Flatten coalesce(colA, coalesce(colB, colC)) to coalesce(colA, colB, colC). --- .../io/prestosql/sql/planner/ExpressionInterpreter.java | 6 ++++++ .../java/io/prestosql/sql/TestExpressionInterpreter.java | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java index 581a98a006c3..690a1d1891e8 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/ExpressionInterpreter.java @@ -549,6 +549,12 @@ protected Object visitCoalesceExpression(CoalesceExpression node, Object context List values = node.getOperands().stream() .map(value -> processWithExceptionHandling(value, context)) .filter(Objects::nonNull) + .flatMap(expression -> { + if (expression instanceof CoalesceExpression) { + return ((CoalesceExpression) expression).getOperands().stream(); + } + return Stream.of(expression); + }) .collect(Collectors.toList()); if ((!values.isEmpty() && !(values.get(0) instanceof Expression)) || values.size() == 1) { diff --git a/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java b/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java index 364c613dc144..3a2810049625 100644 --- a/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java +++ b/presto-main/src/test/java/io/prestosql/sql/TestExpressionInterpreter.java @@ -1145,6 +1145,11 @@ public void testCoalesce() assertOptimizedEquals("coalesce(6, unbound_long2, unbound_long, unbound_long3)", "6"); assertOptimizedEquals("coalesce(2 * 3, unbound_long2, unbound_long, unbound_long3)", "6"); assertOptimizedMatches("coalesce(random(), random(), 5)", "coalesce(random(), random(), 5E0)"); + assertOptimizedMatches("coalesce(unbound_long, coalesce(unbound_long, 1))", "coalesce(unbound_long, BIGINT '1')"); + assertOptimizedMatches("coalesce(coalesce(unbound_long, coalesce(unbound_long, 1)), unbound_long2)", "coalesce(unbound_long, BIGINT '1')"); + assertOptimizedMatches("coalesce(unbound_long, 2, coalesce(unbound_long, 1))", "coalesce(unbound_long, BIGINT '2')"); + assertOptimizedMatches("coalesce(coalesce(unbound_long, coalesce(unbound_long2, unbound_long3)), 1)", "coalesce(unbound_long, unbound_long2, unbound_long3, BIGINT '1')"); + assertOptimizedMatches("coalesce(unbound_double, coalesce(random(), unbound_double))", "coalesce(unbound_double, random())"); } @Test